/*
* ProviewR Open Source Process Control.
* Copyright (C) 2005-2023 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"
/* PLC RUTINER */
Inc3p
void inc3p_init(pwr_sClass_inc3p* object)
{
object->Acc = 0;
}
void inc3p_exec(plc_sThread* tp, pwr_sClass_inc3p* object)
{
/* Clear old orders */
if (object->Open && object->Acc <= 0)
object->Open = FALSE;
if (object->Close && object->Acc >= 0)
object->Close = FALSE;
/* Get Incremental input */
object->OutChange = *object->OutChangeP;
object->Acc += object->OutChange * object->Gain;
if /* Open */
((object->Acc >= object->MinTim)
|| ((object->Open == TRUE) && (object->Acc > 0)
&& (object->TimerFlag == TRUE))) {
*object->CloseP = FALSE;
object->Close = FALSE;
object->TimerDO = object->OpenP;
object->TimerTime = object->Acc;
timer_in(tp, object);
if (object->TimerCount > 0) {
object->Open = TRUE;
*object->OpenP = TRUE;
object->AccTim = 0;
if (object->Acc > *object->ScanTime)
object->Acc -= *object->ScanTime;
else
object->Acc = 0;
} else {
object->Open = FALSE;
*object->OpenP = TRUE;
object->TimerCount = 0;
object->AccTim += *object->ScanTime;
}
} else if /* Close */
((object->Acc <= -object->MinTim)
|| ((object->Close == TRUE) && (object->Acc < 0)
&& (object->TimerFlag == TRUE))) {
*object->OpenP = FALSE;
object->Open = FALSE;
object->TimerDO = object->CloseP;
object->TimerTime = -object->Acc;
timer_in(tp, object);
if (object->TimerCount > 0) {
object->Close = TRUE;
*object->CloseP = TRUE;
object->AccTim = 0;
if (object->Acc < -*object->ScanTime)
object->Acc += *object->ScanTime;
else
object->Acc = 0;
} else {
object->Close = FALSE;
*object->CloseP = TRUE;
object->TimerCount = 0;
object->AccTim += *object->ScanTime;
}
} else /* No output */
{
object->Open = FALSE;
*object->OpenP = FALSE;
object->Close = FALSE;
*object->CloseP = FALSE;
object->TimerCount = 0;
if (object->MaxTim > 0) /* Limit integration */
{
object->AccTim += *object->ScanTime;
if (object->AccTim >= object->MaxTim) {
object->Acc = 0;
object->AccTim = 0;
}
}
}
/* Limit output 2014-02-18 / Werner */
if ((object->Acc > object->MaxWindup) && (object->MaxWindup > 0.0))
object->Acc = object->MaxWindup;
if ((object->Acc < -object->MaxWindup) && (object->MaxWindup > 0.0))
object->Acc = -object->MaxWindup;
}
Pos3p
void pos3p_exec(plc_sThread* tp, pwr_sClass_pos3p* object)
{
float error;
/* Get Setpoint and pos */
object->OutVal = *object->OutValP;
object->Pos = *object->PosP;
error = *object->OutValP - *object->PosP;
/* Open ? */
if ((error > object->ErrSta)
|| ((object->Open == TRUE) && (error > object->ErrSto))) {
*object->CloseP = FALSE;
object->Close = FALSE;
object->Open = TRUE;
object->TimerDO = object->OpenP;
object->TimerTime = error * object->Gain;
if ((error <= object->ErrSta) && (*object->OpenP == FALSE))
object->TimerTime = 0;
timer_in(tp, object);
if (object->TimerCount > 0) {
*object->OpenP = TRUE;
} else {
*object->OpenP = FALSE;
object->TimerCount = 0;
}
}
/* Close ? */
else if ((error < -object->ErrSta)
|| ((object->Close == TRUE) && (error < -object->ErrSto))) {
*object->OpenP = FALSE;
object->Open = FALSE;
object->Close = TRUE;
object->TimerDO = object->CloseP;
object->TimerTime = -error * object->Gain;
if ((error >= -object->ErrSta) && (*object->CloseP == FALSE))
object->TimerTime = 0;
timer_in(tp, object);
if (object->TimerCount > 0) {
*object->CloseP = TRUE;
} else {
*object->CloseP = FALSE;
object->TimerCount = 0;
}
} else {
/* No output */
object->Open = FALSE;
*object->OpenP = FALSE;
object->Close = FALSE;
*object->CloseP = FALSE;
object->TimerCount = 0;
}
}
Out2p
void out2p_exec(plc_sThread* tp, pwr_sClass_out2p* object)
{
float ontime;
float offtime;
/* Get Input */
object->OutVal = *object->OutValP;
if (object->MaxOut > object->MinOut) {
ontime = (object->OutVal - object->MinOut)
/ (object->MaxOut - object->MinOut) * object->Period;
offtime = object->Period - ontime;
} else {
ontime = 0;
offtime = 0;
}
/* Increase Running-time */
object->RunTime += *object->ScanTime;
/* Should we turn off ? */
if (object->Order == TRUE) {
if ((object->RunTime > ontime) && (object->OutVal < object->MaxOut)) {
object->Order = FALSE;
object->RunTime = 0;
}
}
/* Should we turn on ? */
else {
if ((object->RunTime > offtime) && (object->OutVal > object->MinOut)) {
object->Order = TRUE;
object->RunTime = 0;
}
}
}
Mode
void mode_exec(plc_sThread* tp, pwr_sClass_mode* object)
{
/* Get indata */
object->ProcVal = *object->ProcValP;
object->XSetVal = *object->XSetValP;
object->XForcVal = *object->XForcValP;
object->Forc1 = *object->Forc1P;
object->Forc2 = *object->Forc2P;
object->OutVal = *object->OutValP;
/* Make appropriate actions, depending on actual mode */
/* Manual */
if (object->OpMod <= 1) {
object->Force = TRUE;
object->ManMode = TRUE;
object->AutMode = FALSE;
object->CascMod = FALSE;
/* External setpoint ? */
if ((object->AccMod & 2) == 0)
object->SetVal = object->XSetVal;
/* Test if Force in manual mode */
if (object->Forc1)
object->ForcVal = object->XForcVal;
else {
if (object->ForcVal < object->MinOut)
object->ForcVal = object->MinOut;
else if (object->ForcVal > object->MaxOut)
object->ForcVal = object->MaxOut;
}
} else
/* Not Manual Mode */
{
/* Auto */
if (object->OpMod == 2) {
object->ManMode = FALSE;
object->AutMode = TRUE;
object->CascMod = FALSE;
}
/* Cascade mode */
else {
object->ManMode = FALSE;
object->AutMode = FALSE;
object->CascMod = TRUE;
object->SetVal = object->XSetVal;
}
/* Test if force in Auto or Cascade */
if (object->Forc1 || object->Forc2) {
object->Force = TRUE;
object->ForcVal = object->XForcVal;
} else {
object->Force = FALSE;
object->ForcVal = object->OutVal;
}
}
object->Error = object->ProcVal - object->SetVal;
}
Pid
void pid_exec(plc_sThread* tp, pwr_sClass_pid* object)
{
/* Define Algoritm bitmask */
#define IALG 1 /* Integral part -> Incremental algorithm */
#define PALG 2 /* Proportional part exists */
#define PAVV 4 /* Proportional part working on control difference */
#define DALG 8 /* Derivative part exists */
#define DAVV 16 /* Derivative part working on control difference */
//#define IWUP 1 /* Windup limitation on I part */
#define BIWUP 2 /* Windup limitation on Bias and I part */
#define BPIWUP 4 /* Windup limitation on Bias PI part */
#define BPIDWUP 8 /* Windup limitation on Bias and PID part (Default, old funcionality */
float xold; /* Local variables */
float eold;
float bfold;
float ddiff;
float derold;
double ut;
double dut;
float kd;
double absut;
float gain;
/* Save old values */
xold = object->ProcVal;
eold = object->ControlDiff;
bfold = object->Bias;
derold = object->FiltDer;
/* Get Input */
object->ProcVal = *object->ProcValP;
object->SetVal = *object->SetValP;
object->ForcVal = *object->ForcValP;
object->Bias = *object->BiasP;
object->Force = *object->ForceP;
object->IntOff = *object->IntOffP;
/* Calculate Controller Error and Filtered derivate */
object->ControlDiff = object->ProcVal - object->SetVal;
ddiff = ((object->PidAlg & DAVV) != 0)
? (object->ControlDiff - eold) / *object->ScanTime
: (object->ProcVal - xold) / *object->ScanTime;
if (((object->DerGain * *object->ScanTime) >= object->DerTime)
|| (object->DerTime <= 0))
object->FiltDer = ddiff; /* No Filter */
else {
kd = 1.0 / (1.0 + object->DerGain * *object->ScanTime / object->DerTime);
object->FiltDer += (ddiff - derold) * (1.0 - kd);
}
if (object->Inverse == 0)
gain = object->Gain;
else
gain = -object->Gain;
if (object->Force)
/* Force */
{
object->OutChange = object->ForcVal - object->OutVal;
object->OutVal = object->OutWindup = object->ForcVal;
object->EndMin = FALSE;
object->EndMax = FALSE;
/* Adjust for bumpless transfer to auto */
object->PDManOffset = object->OutVal - gain * object->ControlDiff
- object->BiasGain * object->Bias;
if ((object->PidAlg & IALG) != 0)
object->AbsOut = 0.0;
else
object->AbsOut = object->OutVal;
if (object->WindupMask < BIWUP)
object->OutWindup -= object->BiasGain * object->Bias;
if (object->WindupMask < BPIWUP)
object->OutWindup -= gain * object->ControlDiff;
object->AbsOut = object->OutVal - object->OutWindup;
}
else
/* Auto mode */
{
dut = absut = 0.0;
if ((object->PidAlg & IALG) != 0)
/* Incremental algorithm */
{
/* Integral-part */
if ((*object->IntOffP == FALSE) && (object->IntTime > 0))
dut = object->ControlDiff * *object->ScanTime / object->IntTime;
if ((object->PidAlg & PALG) != 0)
dut *= gain;
else
gain = 0.0; /* Pure I-controller */
/* Bias */
if (object->WindupMask >= BIWUP) /* Windup on Bias */
dut += object->BiasGain * (object->Bias - bfold);
else
absut = object->BiasGain * object->Bias;
/* P-part */
if (object->WindupMask >= BPIWUP) /* Windup on P */
dut += ((object->PidAlg & PAVV) != 0)
? gain * (object->ControlDiff - eold)
: gain * (object->ProcVal - xold);
else
absut += gain * object->ControlDiff;
/* Derivative-part */
if ((object->PidAlg & DALG) != 0) {
if (object->WindupMask >= BPIDWUP) /* Windup on D */
dut += gain * (object->FiltDer - derold) * object->DerTime;
else
absut += gain * object->FiltDer * object->DerTime;
}
/* Limit output */
object->OutWindup += dut;
if (object->OutWindup > object->MaxWindup) {
object->OutWindup = object->MaxWindup;
object->EndMax = TRUE;
} else if (object->OutWindup < object->MinWindup) {
object->OutWindup = object->MinWindup;
object->EndMin = TRUE;
}
ut = object->OutWindup + absut;
if (ut > object->MaxOut)
ut = object->MaxOut;
else if (ut < object->MinOut)
ut = object->MinOut;
dut += absut - object->AbsOut;
}
else
/* Nonincremental algorithm */
{
/* P-part */
ut = object->ControlDiff;
/* Derivative-part */
if ((object->PidAlg & DALG) != 0)
ut += object->FiltDer * object->DerTime;
/* Gain */
ut *= gain;
/* Bias and Man offset */
if (object->PDAbsFlag)
object->PDManOffset = 0;
ut += object->BiasGain * object->Bias + object->PDManOffset;
/* Limit output */
if (object->MaxOut > object->MinOut) {
if (ut > object->MaxOut)
ut = object->MaxOut;
else if (ut < object->MinOut)
ut = object->MinOut;
}
dut = ut - object->OutVal;
absut = ut;
}
/* Output Auto */
object->OutChange = dut;
object->OutVal = ut;
object->AbsOut = absut;
}
}