/*
* 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 <stdlib.h>
#include "pwr_baseclasses.h"
#include "co_time.h"
#include "co_cdh.h"
#include "rt_gdh.h"
#include "rt_gdh_msg.h"
#include "rt_csup_msg.h"
#include "rt_csup.h"
lst_sEntry* csup_Init(pwr_tStatus* status, pwr_tObjid tid, float scanTime)
{
lst_sEntry* lh;
pwr_tObjid cid;
pwr_tObjid pid;
csup_sObject* cp;
int tv_sec;
pwr_tFloat32 max_delay;
pwr_dStatus(sts, status, CSUP__SUCCESS);
lh = calloc(1, sizeof(*lh));
if (lh == NULL)
pwr_Return(NULL, sts, CSUP__INSVIRMEM);
lst_Init(NULL, lh, NULL);
for (*sts = gdh_GetClassList(pwr_cClass_CycleSup, &cid); ODD(*sts);
*sts = gdh_GetNextObject(cid, &cid)) {
cp = calloc(1, sizeof(*cp));
cp->aref = cdh_ObjidToAref(cid);
*sts = gdh_DLRefObjectInfoAttrref(&cp->aref, (void**)&cp->o, &cp->dlid);
if (EVEN(*sts))
goto error;
if (ODD(gdh_GetParent(cid, &pid)) && cdh_ObjidIsEqual(pid, tid))
cp->is_owner = 1;
lst_InsertSucc(NULL, lh, &cp->le, cp);
max_delay = cp->o->MaxDelay;
cp->o->DelayLimit.tv_sec = tv_sec = (int)max_delay;
cp->o->DelayLimit.tv_nsec
= (int)((max_delay - (float)tv_sec + FLT_EPSILON) * 1.0e9);
// errh_Info("maxdelay: %f, tv_sec: %d, tv_nsec: %d", cp->o->MaxDelay,
// cp->o->DelayLimit.tv_sec, cp->o->DelayLimit.tv_nsec);
}
if (lst_IsEmpty(NULL, lh))
goto error;
return lh;
error:
csup_Exit(NULL, lh);
return NULL;
}
int csup_Exec(pwr_tStatus* status, lst_sEntry* lh, pwr_tDeltaTime* next_start,
pwr_tDeltaTime* stop, pwr_tTime* now)
{
int action = 0;
csup_sObject* cp;
pwr_tDeltaTime nextLimit;
pwr_dStatus(sts, status, CSUP__SUCCESS);
if (lh == NULL || next_start == NULL || now == NULL || stop == NULL)
pwr_Return(NO, sts, CSUP__NULPOINT);
while ((cp = lst_Succ(NULL, lh, &lh)) != NULL) {
pwr_sClass_CycleSup* o = cp->o;
if (cp->is_owner) {
if (time_Dcomp(&o->NextLimit, NULL) > 0) {
if (time_Dcomp(stop, &o->NextLimit) > 0) {
if (!o->Delayed) {
o->DelayedTime = *now;
o->Delayed = TRUE;
o->Timely = FALSE;
}
o->DelayCount++;
o->LastDelay = *now;
action = MAX(action, o->DelayAction);
} else if (!o->Timely) {
o->Timely = TRUE;
o->TimelyTime = *now;
}
}
if ((o->DelayLimit.tv_sec & 1 << 31)
!= (o->DelayLimit.tv_nsec & 1 << 31)) {
// printf("DelayLimit.tv_sec: %d, DelayLimit.tv_nsec: %d\n",
// o->DelayLimit.tv_sec, o->DelayLimit.tv_nsec);
errh_Info("DelayLimit.tv_sec: %d, DelayLimit.tv_nsec: %d",
o->DelayLimit.tv_sec, o->DelayLimit.tv_nsec);
}
if ((next_start->tv_sec & 1 << 31) != (next_start->tv_nsec & 1 << 31)) {
// printf("next_start->tv_sec: %d, next_start->tv_nsec: %d\n",
// next_start->tv_sec, next_start->tv_nsec);
errh_Info("next_start->tv_sec: %d, next_start->tv_nsec: %d",
next_start->tv_sec, next_start->tv_nsec);
}
time_Dadd(&nextLimit, next_start, &o->DelayLimit);
/* If we update the tv_nsec field first it is
possible that emon will detect a slip even if it is not. */
o->NextLimit.tv_sec = nextLimit.tv_sec;
o->NextLimit.tv_nsec = nextLimit.tv_nsec;
o->CycleCount++;
} else {
/* Not owner, check stall delay */
if (o->DelayAction == pwr_eSupDelayActionEnum_EmergencyBreak)
{
nextLimit.tv_nsec = o->NextLimit.tv_nsec;
nextLimit.tv_sec = o->NextLimit.tv_sec;
if (time_Dcomp(&nextLimit, NULL) > 0
&& time_Dcomp(stop, &nextLimit) > 0) {
o->DelayCount++;
o->LastDelay = *now;
action = MAX(action, o->DelayAction);
}
}
}
}
return action;
}
void csup_Exit(pwr_tStatus* status, lst_sEntry* lh)
{
csup_sObject* cp;
pwr_dStatus(sts, status, CSUP__SUCCESS);
if (lh == NULL)
return;
while ((cp = lst_Succ(NULL, lh, NULL)) != NULL) {
lst_Remove(NULL, &cp->le);
free(cp);
}
free(lh);
}