/*
 * ProviewR   Open Source Process Control.
 * Copyright (C) 2005-2025 SSAB EMEA AB.
 *
 * This file is part of ProviewR.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ProviewR. If not, see <http://www.gnu.org/licenses/>
 *
 * Linking ProviewR statically or dynamically with other modules is
 * making a combined work based on ProviewR. Thus, the terms and
 * conditions of the GNU General Public License cover the whole
 * combination.
 *
 * In addition, as a special exception, the copyright holders of
 * ProviewR give you permission to, from the build function in the
 * ProviewR Configurator, combine ProviewR with modules generated by the
 * ProviewR PLC Editor to a PLC program, regardless of the license
 * terms of these modules. You may copy and distribute the resulting
 * combined work under the terms of your choice, provided that every
 * copy of the combined work is accompanied by a complete copy of
 * the source code of ProviewR (the version used to produce the
 * combined work), being distributed under the terms of the GNU
 * General Public License plus this exception.
 */
#include "rt_plc.h"
#include "rt_plc_drive.h"

Drive


void drive_exec(plc_sThread* tp, pwr_sClass_drive* object)
{
  pwr_tBoolean start;
  pwr_tBoolean stop;
  pwr_tBoolean Aold;
  pwr_tBoolean Tflag;
  /* Save old values for flank detect */
  Aold = object->AutoStart;
  Tflag = object->TimerFlag;
  /* New Input */
  object->AutoStart = *object->AutoStartP;
  object->AutoNoStop = *object->AutoNoStopP;
  object->Local = *object->LocalP;
  object->ConOn = *object->ConOnP;
  object->Speed = *object->SpeedP;
  object->SafeStop = *object->SafeStopP;
  object->ProdStop = *object->ProdStopP;
  object->NoStart = *object->NoStartP;
  timer2_scan(tp, object);
  /* Is there a stop condition ? */
  stop = object->SafeStop;
  if (object->Local) {
    object->LocStart = *object->LocStartP;
    object->LocDrive = *object->LocDriveP;
    if (!object->LocDrive || (!object->LocStart && !object->ConOn))
      stop = TRUE;
  } else {
    if (object->ProdStop)
      stop = TRUE;
    if (!object->ManMode && !object->AutoNoStop)
      stop = TRUE;
    if (object->ManMode && object->ManStp)
      stop = TRUE;
    /* Check Alarms */
    if (object->Order && !stop) {
      if (!object->ConOn) {
        if (object->Status == 2 && !Tflag) {
          object->Alarm1 = TRUE;
          stop = TRUE;
        } else if (object->Status > 2) {
          object->Alarm2 = TRUE;
          stop = TRUE;
        }
      } else if (!object->Speed && (object->Status >= 3) && !Tflag) {
        object->Alarm3 = TRUE;
        stop = TRUE;
      }
    }
  }
  /* Start condition ? */
  if (object->ManMode) {
    start = object->ManSta;
  } else {
    start = object->AutoStart && !Aold;
  }
  /* Remove alarms when new start */
  if (start) {
    object->Alarm1 = FALSE;
    object->Alarm2 = FALSE;
    object->Alarm3 = FALSE;
  }
  /* Sum alarm */
  object->SumAlarm = (object->Alarm1 || object->Alarm2 || object->Alarm3);
  /* No start or Local */
  if (object->NoStart)
    start = FALSE;
  if (object->Local)
    start = object->LocStart;
  /* Remove manual start / stop */
  object->ManSta = FALSE;
  object->ManStp = FALSE;
  /* Start or stop */
  if (stop) { /* Stop-condition */
    object->Order = FALSE;
    object->TimerCount = 0;
  } else if (start && !object->Order) { /* New start */
    object->Order = TRUE;
    object->TimerTime = object->ProdTim;
    timer2_in(tp, object);
    object->Status = 2; /* Wait for contactor */
  }
  /* Check mode */
  if (object->Order) {
    if ((object->Status == 2) && object->ConOn) {
      /* Waiting for contactor is finished */
      object->TimerTime = object->SpeedTim;
      timer2_in(tp, object);
      object->Status = 3; /* Wait for Speed */
    } else if ((object->Status == 3) && object->Speed) {
      object->Status = 4; /* Production */
    }
  } else {
    object->Status = object->ConOn || object->Speed;
  }
  /* Stopping or stop */
  /* Indication production */
  object->Ind = (object->Status == 4) && !object->Local;
}

Valve


void valve_exec(plc_sThread* tp, pwr_sClass_valve* object)
{
  pwr_tBoolean open;
  /* Get Input-signals */
  object->AutoOpen = *object->AutoOpenP;
  object->EndOpen = *object->EndOpenP;
  object->EndClose = *object->EndCloseP;
  object->Local = *object->LocalP;
  object->LocalOpen = *object->LocalOpenP;
  object->LocalClose = *object->LocalCloseP;
  object->SafeOpen = *object->SafeOpenP;
  object->SafeClose = *object->SafeCloseP;
  object->ProdOpen = *object->ProdOpenP;
  object->ProdClose = *object->ProdCloseP;
  timer2_scan(tp, object);
  /* Check different orders after priority */
  if (object->SafeClose) {
    open = FALSE;
  } else if (object->SafeOpen) {
    open = TRUE;
  } else if (object->Local) {
    if (object->LocalClose) {
      open = FALSE;
    } else if (object->LocalOpen) {
      open = TRUE;
    } else {
      open = object->OrderOpen;
    }
  } else {
    if (object->ProdClose) {
      open = FALSE;
    } else if (object->ProdOpen) {
      open = TRUE;
    } else if (object->ManMode) {
      if (object->ManClose) {
        open = FALSE;
      } else if (object->ManOpen) {
        open = TRUE;
      } else {
        open = object->OrderOpen;
      }
    } else {
      open = object->AutoOpen;
    }
  }
  /* Check if new order */
  if (open != object->OrderOpen) {
    object->Status = open; /* Opening / Closing */
    timer2_in(tp, object);
  }
  /* Check if alarms */
  if (object->Status < 2) {
    if (open) {
      if (object->EndOpen) {
        object->Status = 3; /* Open */
      } else if (!object->TimerFlag) {
        object->Alarm1 = TRUE;
      }
    } else {
      if (object->EndClose) {
        object->Status = 2; /* Closed */
      } else if (!object->TimerFlag) {
        object->Alarm2 = TRUE;
      }
    }
  } else {
    if (open && (!object->EndOpen || object->EndClose)) {
      object->Alarm3 = TRUE; /* No longer Open */
    }
    if (!open && (!object->EndClose || object->EndOpen)) {
      object->Alarm4 = TRUE; /* No longer closed */
    }
  }
  /* Remove alarms if OK */
  if (object->Alarm1 && (!open || object->EndOpen)) {
    object->Alarm1 = FALSE;
  }
  if (object->Alarm2 && (open || object->EndClose)) {
    object->Alarm2 = FALSE;
  }
  if (object->Alarm3 && (!open || (object->EndOpen && !object->EndClose))) {
    object->Alarm3 = FALSE;
  }
  if (object->Alarm4 && (open || (!object->EndOpen && object->EndClose))) {
    object->Alarm4 = FALSE;
  }
  /* Sum Alarm */
  object->SumAlarm
      = object->Alarm1 || object->Alarm2 || object->Alarm3 || object->Alarm4;
  /* Set order and indications */
  object->OrderOpen = open;
  object->IndOpen
      = open && object->EndOpen && !object->EndClose && !object->Local;
  object->IndClose
      = !open && object->EndClose && !object->EndOpen && !object->Local;
  /* Remove Manual orders */
  object->ManOpen = FALSE;
  object->ManClose = FALSE;
}

MValve


void mvalve_exec(plc_sThread* tp, pwr_sClass_mvalve* object)
{
  pwr_tBoolean open;
  pwr_tBoolean close;
  pwr_tBoolean oold;
  pwr_tBoolean cold;
  /* Save old orders for flank detect */
  oold = object->AutoOpen || object->ProdOpen || object->SafeOpen;
  cold = object->AutoClose || object->ProdClose || object->SafeClose;
  /* Get new input-data */
  object->AutoOpen = *object->AutoOpenP;
  object->AutoClose = *object->AutoCloseP;
  object->EndOpen = *object->EndOpenP;
  object->EndClose = *object->EndCloseP;
  object->ConOpen = *object->ConOpenP;
  object->ConClose = *object->ConCloseP;
  object->Local = *object->LocalP;
  object->SafeOpen = *object->SafeOpenP;
  object->SafeClose = *object->SafeCloseP;
  object->SafeStop = *object->SafeStopP;
  object->ProdOpen = *object->ProdOpenP;
  object->ProdClose = *object->ProdCloseP;
  object->ProdStop = *object->ProdStopP;
  timer2_scan(tp, object);
  /* Remove old alarms */
  if (((object->AutoOpen || object->ProdOpen || object->SafeOpen) && !oold)
      || object->ManOpen || object->ManStop) {
    object->Alarm5 = FALSE; /* New open */
  }
  if (((object->AutoClose || object->ProdClose || object->SafeClose) && !cold)
      || object->ManClose || object->ManStop) {
    object->Alarm6 = FALSE; /* New Close */
  }
  if (object->EndOpen) {
    object->Alarm1 = FALSE;
    if (!object->EndClose)
      object->Alarm3 = FALSE;
  }
  if (object->EndClose) {
    object->Alarm2 = FALSE;
    if (!object->EndOpen)
      object->Alarm4 = FALSE;
  }
  if (object->OrderClose) {
    object->Alarm1 = FALSE;
    object->Alarm3 = FALSE;
    object->Alarm5 = FALSE;
  }
  if (object->OrderOpen) {
    object->Alarm2 = FALSE;
    object->Alarm4 = FALSE;
    object->Alarm6 = FALSE;
  }
  /* Evaluate orders */
  /* Safe conditions */
  if (object->SafeStop) {
    open = FALSE;
    close = FALSE;
  } else if (object->SafeClose) {
    open = FALSE;
    close = !object->Alarm6;
  } else if (object->SafeOpen) {
    close = FALSE;
    open = !object->Alarm5;
  } else if (object->Local) {
    /* Local */
    object->LocalOpen = *object->LocalOpenP;
    object->LocalClose = *object->LocalCloseP;
    object->LocalStop = *object->LocalStopP;
    if (object->LocalStop) {
      open = FALSE;
      close = FALSE;
    } else if (object->LocalClose) {
      open = FALSE;
      close = TRUE;
    } else if (object->LocalOpen) {
      open = TRUE;
      close = FALSE;
    } else if (object->OrderOpen) {
      close = FALSE;
      open = object->ConOpen;
    } else if (object->OrderClose) {
      open = FALSE;
      close = object->ConClose;
    } else {
      open = FALSE;
      close = FALSE;
    }
  } else if (object->ProdStop) {
    /* Production  requirement conditions */
    open = FALSE;
    close = FALSE;
  } else if (object->ProdClose) {
    open = FALSE;
    close = !object->Alarm6;
  } else if (object->ProdOpen) {
    close = FALSE;
    open = !object->Alarm5;
  } else if (object->ManMode) {
    /* Manual */
    if (object->ManStop) {
      open = FALSE;
      close = FALSE;
    } else if (object->ManClose) {
      open = FALSE;
      close = TRUE;
    } else if (object->ManOpen) {
      open = TRUE;
      close = FALSE;
    } else if (object->OrderOpen) {
      close = FALSE;
      open = !object->Alarm5;
    } else if (object->OrderClose) {
      open = FALSE;
      close = !object->Alarm6;
    } else {
      open = FALSE;
      close = FALSE;
    }
  } else {
    /* Automatic */
    if (object->AutoClose) {
      open = FALSE;
      close = !object->Alarm6;
    } else if (object->AutoOpen) {
      close = FALSE;
      open = !object->Alarm5;
    } else {
      open = FALSE;
      close = FALSE;
    }
  }
  /* Check if Error end-positions */
  if ((object->Status == 3) && (!object->EndOpen || object->EndClose)) {
    object->Alarm3 = TRUE;
    object->Status = 0;
  }
  if ((object->Status == -3) && (object->EndOpen || !object->EndClose)) {
    object->Alarm4 = TRUE;
    object->Status = 0;
  }
  if (object->EndOpen && object->EndClose) {
    object->Alarm3 = TRUE;
    object->Alarm4 = TRUE;
  }
  /* Set output order and indications */
  /* New open order */
  if (open && !object->OrderOpen) {
    if (object->ConClose) {
      object->Status = 0;
    } else {
      object->TimerTime = object->Ctime;
      timer2_in(tp, object);
      object->Status = 1;
    }
  }
  /* New close order */
  if (close && !object->OrderClose) {
    if (object->ConOpen) {
      object->Status = 0;
    } else {
      object->TimerTime = object->Ctime;
      timer2_in(tp, object);
      object->Status = -1;
    }
  }
  if (!open && !close && (object->Status > -3) && (object->Status < 3)) {
    object->Status = 0;
  }
  object->OrderOpen
      = (object->EndOpen || close || (object->Status <= 0)) ? FALSE : open;
  object->OrderClose
      = (object->EndClose || (object->Status >= 0)) ? FALSE : close;
  object->IndOpen
      = object->EndOpen && !object->EndClose && !close && !object->Local;
  object->IndClosed
      = !object->EndOpen && object->EndClose && !open && !object->Local;
  /* Alarm for contactor and time */
  if (object->OrderOpen) {
    if (!object->ConOpen && ((object->Status > 1) || !object->TimerFlag)) {
      object->Alarm5 = TRUE;
    }
    if ((object->Status == 1) && object->ConOpen && !object->TimerFlag) {
      object->TimerTime = object->RunTime;
      timer2_in(tp, object);
      object->Status = 2;
    }
    if ((object->Status == 2) && !object->TimerFlag && (object->RunTime > 0)) {
      object->Alarm1 = TRUE;
    }
  } else if (object->OrderClose) {
    if (!object->ConClose && ((object->Status < -1) || !object->TimerFlag)) {
      object->Alarm6 = TRUE;
    }
    if ((object->Status == -1) && object->ConClose && !object->TimerFlag) {
      object->TimerTime = object->RunTime;
      timer2_in(tp, object);
      object->Status = -2;
    }
    if ((object->Status == -2) && !object->TimerFlag && (object->RunTime > 0)) {
      object->Alarm2 = TRUE;
    }
  }
  if (object->EndOpen && open)
    object->Status = 3;
  if (object->EndClose && close)
    object->Status = -3;
  /* Sum Alarm*/
  object->SumAlarm = object->Alarm1 || object->Alarm2 || object->Alarm3
      || object->Alarm4 || object->Alarm5 || object->Alarm6;
  /* Remove Manual orders */
  object->ManOpen = FALSE;
  object->ManClose = FALSE;
  object->ManStop = FALSE;
}

Posit


void posit_exec(plc_sThread* tp, pwr_sClass_posit* object)
{
  pwr_tBoolean iold; /* old value InPlace */
  pwr_tBoolean aold; /* old value AutoPos */
  iold = object->InPlace;
  aold = object->AutoPos;
  timer2_scan(tp, object);
  /* Read Input data */
  object->PosVal = *object->PosValP;
  if (!object->ManMode)
    object->SetPos = *object->SetPosP;
  object->AutoPos = *object->AutoPosP;
  object->Reset = *object->ResetP;
  /* Calculate Difference and deadzone */
  object->PosError = object->PosVal - object->SetPos;
  if ((object->PosError <= object->DeadZone1)
      && (object->PosError >= -object->DeadZone2)) {
    object->InPlace = TRUE;
  } else {
    object->InPlace = FALSE;
  }
  /* Pos or Reset Order */
  if (object->AutoPos && !aold && !object->ManMode) {
    object->PosOn = TRUE;
    iold = FALSE;
  }
  if (object->Reset)
    object->PosOn = FALSE;
  /* Positioning in progress ? */
  if (object->PosOn) {
    if (object->InPlace) {
      object->Order1 = FALSE;
      object->Order2 = FALSE;
      /* Set up new Time */
      if (!iold && object->TimerTime > 0) {
        timer2_in(tp, object);
      }
      /* Check  if InPos long enough */
      if (!object->TimerFlag && object->TimerTime > 0)
        object->PosOn = FALSE;
    } else if (object->PosError > 0) {
      object->Order1 = TRUE;
      object->Order2 = FALSE;
    } else {
      object->Order1 = FALSE;
      object->Order2 = TRUE;
    }
  } else {
    /* Wait for next order */
    object->Order1 = FALSE;
    object->Order2 = FALSE;
  }
}