/*
 * 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 <math.h>
#include "co_cdh.h"
#include "co_dcli.h"
#include "co_string.h"
#include "co_time.h"
#include "rt_plc.h"
#include "rt_plc_arithm.h"
/* 		PLC RUTINER			*/

Sum


void sum_exec(plc_sThread* tp, pwr_sClass_sum* object)
{
#define sumsize 8
  pwr_tFloat32** ptr; /* Pointer to ptr to input */
  int i; /* Loopindex */
  float sum; /* Result */
  /* Initialize */
  sum = object->Const;
  ptr = &object->In1P;
  /* SUM loop */
  for (i = 0; i < sumsize; i++) {
    if (*ptr != NULL)
      sum += **ptr * object->FVect[i];
    ptr = (pwr_tFloat32**)((char*)ptr + pwr_cInputOffset);
  }
  /* Result */
  object->ActVal = sum;
}

MaxMin


void maxmin_exec(plc_sThread* tp, pwr_sClass_maxmin* object)
{
#define maxminsize 8
#define maxmin_maxfloat 1.E37
#define maxmin_minfloat -1.E37
  float minval; /* Lowest value */
  float maxval; /* Highest value */
  int i; /* Loopcounter */
  float** ptr; /* Pointer to ptr to digin */
  /* Initialize */
  ptr = &object->In1P;
  minval = maxmin_maxfloat;
  maxval = maxmin_minfloat;
  /* MaxMin loop */
  for (i = 0; i < maxminsize; i++) {
    if (*ptr != NULL) {
      if (**ptr > maxval)
        maxval = **ptr;
      if (**ptr < minval)
        minval = **ptr;
    }
    ptr = (pwr_tFloat32**)((char*)ptr + pwr_cInputOffset);
  }
  /* Set Output */
  object->MaxVal = maxval;
  object->MinVal = minval;
}

Limit


void limit_exec(plc_sThread* tp, pwr_sClass_limit* object)
{
  object->Max = *object->MaxP;
  object->Min = *object->MinP;
  object->In = *object->InP;
  if (object->In > object->Max) {
    object->ActVal = object->Max;
    object->High = TRUE;
    object->Low = FALSE;
  } else if (object->In < object->Min) {
    object->Low = TRUE;
    if (object->Min <= object->Max) {
      object->ActVal = object->Min;
      object->High = FALSE;
    } else {
      object->ActVal = object->Max;
      object->High = TRUE;
    }
  } else {
    object->ActVal = object->In;
    object->High = FALSE;
    object->Low = FALSE;
  }
}

Comph


void comph_exec(plc_sThread* tp, pwr_sClass_comph* object)
{
  object->In = *object->InP;
  object->Lim = *object->LimP;
  if (object->High) {
    if (object->In <= (object->Lim - object->Hysteres))
      object->High = FALSE;
  } else if (object->In > object->Lim)
    object->High = TRUE;
}

Compl


void compl_exec(plc_sThread* tp, pwr_sClass_compl* object)
{
  object->In = *object->InP;
  object->Lim = *object->LimP;
  if (object->Low) {
    if (object->In >= (object->Lim + object->Hysteres))
      object->Low = FALSE;
  } else if (object->In < object->Lim)
    object->Low = TRUE;
}

Select


void select_exec(plc_sThread* tp, pwr_sClass_select* object)
{
  object->Control = *object->ControlP;
  if (object->Control) {
    object->ActVal = *object->HighP;
    object->NotActVal = *object->LowP;
  } else {
    object->ActVal = *object->LowP;
    object->NotActVal = *object->HighP;
  }
}

Ramp


void ramp_init(pwr_sClass_ramp* object)
{
  object->In = *object->InP;
  object->ActVal = object->In;
}
/* New ramp_exec with RampUp used when absolute value is increasing */
/*  and RampDown used when absolute value is decreasing */
/* New functionality will be used when new Boolean parameter 'RampUpAbs' */
/*  is on. For Proview 3.0 we will use 'AccUp' not zero for new functionality */
/* If this parameter is zero the routine will workin the old way with */
/*  RampUp for increasing and RampDown for decreasing */
/* 2001-03-13 Hans Werner */
void ramp_exec(plc_sThread* tp, pwr_sClass_ramp* object)
{
  float limit1; /* First limit */
  float limit2; /* Second limit at sign change */
  float old; /* Actual start value */
  float out; /* New output */
  float scan; /* scantime in seconds */
  /* Assume new output as unlimited as a start */
  out = object->In = *object->InP; /* Get aimed value */
  old = *object->FeedBP; /* Startvalue */
  scan = *object->ScanTime; /* Get scantime */
  if (out > old) /* Increase */
  {
    if (old >= 0.0) /* Positive rising even more */
    {
      limit1 = scan * object->RampUp;
      if ((limit1 > 0.0) && (out > old + limit1))
        out = old + limit1;
    } else if (out <= 0.0) /* Negative rising towards zero */
    {
      if (object->RampUpAbs)
        limit1 = scan * object->RampDown;
      else
        limit1 = scan * object->RampUp;
      if ((limit1 > 0.0) && (out > old + limit1))
        out = old + limit1;
    } else /* From Neg to Pos */
    {
      if (object->RampUpAbs) {
        limit1 = scan * object->RampDown;
        limit2 = scan * object->RampUp;
      } else
        limit1 = limit2 = scan * object->RampUp;
      if (limit1 <= 0.0) /* No limit up to zero */
      {
        if ((limit2 > 0.0) && (out > limit2))
          out = limit2;
      } else if (old <= -limit1) /* Will still not reach zero */
      {
        if (out > old + limit1)
          out = old + limit1;
      } else if (limit2 > 0) /* Use second limitation above zero */
      {
        if (old < -limit2)
          out = 0.0;
        else if (out > old + limit2)
          out = old + limit2;
      }
    } /* End Neg to Pos */
  } /* End Increasing */
  else if (out < old) /* Decrease */
  {
    if (old <= 0.0) /* Negative falling even more */
    {
      if (object->RampUpAbs)
        limit1 = scan * object->RampUp;
      else
        limit1 = scan * object->RampDown;
      if ((limit1 > 0.0) && (out < old - limit1))
        out = old - limit1;
    } else if (out >= 0.0) /* Positive falling towards zero */
    {
      limit1 = scan * object->RampDown;
      if ((limit1 > 0.0) && (out < old - limit1))
        out = old - limit1;
    } else /* From Pos to Neg */
    {
      if (object->RampUpAbs) {
        limit1 = scan * object->RampDown;
        limit2 = scan * object->RampUp;
      } else
        limit1 = limit2 = scan * object->RampDown;
      if (limit1 <= 0.0) /* No limit down to zero */
      {
        if ((limit2 > 0.0) && (out < -limit2))
          out = -limit2;
      } else if (old >= limit1) /* Will still not reach zero */
      {
        if (out < old - limit1)
          out = old - limit1;
      } else if (limit2 > 0) /* Use second limitation below zero */
      {
        if (old > limit2)
          out = 0.0;
        else if (out < old - limit2)
          out = old - limit2;
      }
    } /* End Neg to Pos */
  } /* End Decreasing */
  object->ActVal = out; /* New output */
}

Filter


void filter_init(pwr_sClass_filter* object)
{
  object->In = *object->InP;
  object->ActVal = object->In;
}
void filter_exec(plc_sThread* tp, pwr_sClass_filter* object)
{
  float kd;
  object->In = *object->InP;
  if (object->FiltCon > 0.0) {
    kd = 1.0 / (1.0 + *object->ScanTime / object->FiltCon);
    object->ActVal
        = *object->FeedBP + (object->In - *object->FeedBP) * (1.0 - kd);
  } else
    object->ActVal = object->In;
}

Speed


void speed_exec(plc_sThread* tp, pwr_sClass_speed* object)
{
  float old;
  old = object->In;
  object->In = *object->InP;
  object->ActVal = (object->In - old) / *object->ScanTime * object->TimFact;
}

Timint


void timint_exec(plc_sThread* tp, pwr_sClass_timint* object)
{
  /* Clear ? */
  if (*object->ClearP && !object->Clear) {
    object->OldAcc = object->ActVal;
    object->ActVal = 0;
  }
  object->Clear = *object->ClearP;
  /* Ackumulate new value */
  object->ActVal += *object->InP * *object->ScanTime / object->TimFact;
}

TimeMean


void timemean_exec(plc_sThread* tp, pwr_sClass_timemean* o)
{
  if (*o->ResetP && !o->Reset) {
    /* Reset */
    o->ActVal = o->AccMean;
    o->AccTime = 0;
  }
  o->Reset = *o->ResetP;
  /* Calculate new value */
  o->AccMean = (*o->InP * *o->ScanTime + o->AccMean * o->AccTime)
      / (*o->ScanTime + o->AccTime);
  o->AccTime += *o->ScanTime;
}

Curve


void curve_exec(plc_sThread* tp, pwr_sClass_curve* object)
{
  float x0 = 0.0;
  float x1;
  float y0 = 0.0;
  float y1;
  float number;
  float* tabpointer;
  /* Get input */
  object->In = *object->InP;
  if (object->TabP == NULL)
    object->ActVal = object->In;
  else
  /* Get pointer to table, and number of pairs in table */
  {
    tabpointer = object->TabP;
    number = *tabpointer++;
    if (number <= 0)
      object->ActVal = object->In;
    else
    /* Search in table */
    {
      x1 = *tabpointer++;
      y1 = *tabpointer++;
      if (object->In <= x1)
        object->ActVal = y1;
      else {
        for (; (number > 1) && (object->In > x1); number--) {
          x0 = x1;
          x1 = *tabpointer++;
          y0 = y1;
          y1 = *tabpointer++;
        }
        if (object->In > x1)
          object->ActVal = y1; /* End of table */
        else
          object->ActVal = y0
              + (y1 - y0) * (object->In - x0) / (x1 - x0); /* Interpollation */
      }
    }
  }
}

Adelay


void adelay_init(pwr_sClass_adelay* object)
{
  object->StoredNumbers = 0;
  object->StoInd = -1;
  object->Count = object->MaxCount;
  object->ActVal = *object->InP;
}
void adelay_exec(plc_sThread* tp, pwr_sClass_adelay* object)
{
  /* Local variables */
  int maxindex;
  int actindex;
  int actoff;
  maxindex = sizeof(object->TimVect) / 4;
  /*		Get input
  */
  object->In = *object->InP;
  object->Tim = *object->TimP;
  /*		Calculate average in each storeplace.
                  MaxCount is number of cycles before each shift
  */
  object->Count++;
  if (object->Count >= object->MaxCount) {
    object->StoInd++;
    if ((object->StoInd >= maxindex) || (object->StoInd < 0))
      object->StoInd = 0;
    if (object->StoredNumbers < maxindex)
      object->StoredNumbers++;
    object->Count = 0;
    object->TimVect[object->StoInd] = object->In;
  } else
    object->TimVect[object->StoInd]
        = (object->TimVect[object->StoInd] * object->Count + object->In)
        / (object->Count + 1);
  /*		Calculate position for output
  */
  actoff = (object->Tim / tp->f_scan_time - object->Count) / object->MaxCount;
  if (actoff >= object->StoredNumbers)
    actoff = object->StoredNumbers - 1;
  actindex = object->StoInd - actoff;
  if (actindex < 0)
    actindex += maxindex;
  object->ActVal = object->TimVect[actindex];
}

PiSpeed


void pispeed_init(pwr_sClass_pispeed* object)
{
  /* Read input */
  object->PulsIn = *object->PulsInP;
}
void pispeed_exec(plc_sThread* tp, pwr_sClass_pispeed* object)
{
  int piold; /* Old input */
  /* Read input */
  piold = object->PulsIn;
  object->PulsIn = *object->PulsInP;
  /* Calculate flow */
  object->ActVal = (object->PulsIn - piold) * object->Gain * object->TimFact
      / *object->ScanTime;
}

DtoMask


void DtoMask_exec(plc_sThread* tp, pwr_sClass_DtoMask* object)
{
  int i;
  pwr_tBoolean *d, **dp;
  pwr_tInt32 val = 0;
  pwr_tInt32 m = 1;
  d = &object->d1;
  dp = &object->d1P;
  for (i = 0; i < 32; i++) {
    *d = **dp;
    if (*d)
      val |= m;
    d = (pwr_tBoolean*)((char*)d + pwr_cInputOffset);
    dp = (pwr_tBoolean**)((char*)dp + pwr_cInputOffset);
    m = m << 1;
  }
  object->Mask = val;
}

MaskToD


void MaskToD_exec(plc_sThread* tp, pwr_sClass_MaskToD* object)
{
  int i;
  pwr_tInt32 m = 1;
  pwr_tBoolean* d;
  d = &object->od1;
  object->Mask = *object->MaskP;
  for (i = 0; i < 32; i++) {
    if (object->Mask & m)
      *d = TRUE;
    else
      *d = FALSE;
    d++;
    m = m << 1;
  }
}

DtoEnum


void DtoEnum_exec(plc_sThread* tp, pwr_sClass_DtoEnum* object)
{
  int i;
  pwr_tBoolean *d, **dp;
  pwr_tInt32 val = object->DefaultValue;
  d = &object->d0;
  dp = &object->d0P;
  for (i = 0; i < 32; i++) {
    *d = **dp;
    if (*d) {
      val = object->EnumValues[i];
      break;
    }
    d = (pwr_tBoolean*)((char*)d + pwr_cInputOffset);
    dp = (pwr_tBoolean**)((char*)dp + pwr_cInputOffset);
  }
  object->Enum = val;
}

EnumToD


void EnumToD_exec(plc_sThread* tp, pwr_sClass_EnumToD* object)
{
  int i;
  pwr_tBoolean* d;
  d = &object->od0;
  object->Enum = *object->EnumP;
  for (i = 0; i < 32; i++) {
    if (object->Enum == object->EnumValues[i])
      *d = TRUE;
    else
      *d = FALSE;
    d++;
  }
}

Mod


void Mod_exec(plc_sThread* tp, pwr_sClass_Mod* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->ActVal = fmodf(o->In1, o->In2);
}

Equal


void Equal_exec(plc_sThread* tp, pwr_sClass_Equal* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->Status = (fabsf(o->In1 - o->In2) < FLT_EPSILON);
}

NotEqual


void NotEqual_exec(plc_sThread* tp, pwr_sClass_NotEqual* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->Status = !(fabsf(o->In1 - o->In2) < FLT_EPSILON);
}

GreaterEqual


void GreaterEqual_exec(plc_sThread* tp, pwr_sClass_GreaterEqual* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->Status = (o->In1 >= o->In2) || (fabsf(o->In1 - o->In2) < FLT_EPSILON);
}

GreaterThan


void GreaterThan_exec(plc_sThread* tp, pwr_sClass_GreaterThan* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->Status = (o->In1 > o->In2);
}

LessEqual


void LessEqual_exec(plc_sThread* tp, pwr_sClass_LessEqual* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->Status = (o->In1 <= o->In2) || (fabsf(o->In1 - o->In2) < FLT_EPSILON);
}

LessThan


void LessThan_exec(plc_sThread* tp, pwr_sClass_LessThan* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->Status = (o->In1 < o->In2);
}

IEqual


void IEqual_exec(plc_sThread* tp, pwr_sClass_IEqual* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->Status = (o->In1 == o->In2);
}

INotEqual


void INotEqual_exec(plc_sThread* tp, pwr_sClass_INotEqual* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->Status = (o->In1 != o->In2);
}

IGreaterEqual


void IGreaterEqual_exec(plc_sThread* tp, pwr_sClass_IGreaterEqual* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->Status = (o->In1 >= o->In2);
}

IGreaterThan


void IGreaterThan_exec(plc_sThread* tp, pwr_sClass_IGreaterThan* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->Status = (o->In1 > o->In2);
}

ILessEqual


void ILessEqual_exec(plc_sThread* tp, pwr_sClass_ILessEqual* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->Status = (o->In1 <= o->In2);
}

ILessThan


void ILessThan_exec(plc_sThread* tp, pwr_sClass_ILessThan* o)
{
  o->In1 = *o->In1P;
  o->In2 = *o->In2P;
  o->Status = (o->In1 < o->In2);
}

IAdd


void IAdd_exec(plc_sThread* tp, pwr_sClass_IAdd* o)
{
#define IADD_SIZE 8
  int i;
  pwr_tInt32** inp = &o->In1P;
  pwr_tInt32 sum = 0;
  for (i = 0; i < IADD_SIZE; i++) {
    sum += **inp;
    inp = (pwr_tInt32**)((char*)inp + pwr_cInputOffset);
  }
  o->ActVal = sum;
}

IMul


void IMul_exec(plc_sThread* tp, pwr_sClass_IMul* o)
{
#define IMUL_SIZE 8
  int i;
  pwr_tInt32** inp = &o->In1P;
  pwr_tInt32 result = **inp;
  for (i = 1; i < IMUL_SIZE; i++) {
    inp = (pwr_tInt32**)((char*)inp + pwr_cInputOffset);
    result *= **inp;
  }
  o->ActVal = result;
}

ISub


void ISub_exec(plc_sThread* tp, pwr_sClass_ISub* o)
{
  o->ActVal = *o->In1P - *o->In2P;
}

IDiv


void IDiv_exec(plc_sThread* tp, pwr_sClass_IDiv* o)
{
  if (*o->In2P == 0)
    o->ActVal = 0;
  else
    o->ActVal = *o->In1P / *o->In2P;
}

IMax


void IMax_exec(plc_sThread* tp, pwr_sClass_IMax* o)
{
#define IMAX_SIZE 8
  int i;
  pwr_tInt32** inp = &o->In1P;
  pwr_tInt32 result = INT_MIN;
  for (i = 0; i < IMAX_SIZE; i++) {
    if (**inp > result)
      result = **inp;
    inp = (pwr_tInt32**)((char*)inp + pwr_cInputOffset);
  }
  o->ActVal = result;
}

IMin


void IMin_exec(plc_sThread* tp, pwr_sClass_IMin* o)
{
#define IMIN_SIZE 8
  int i;
  pwr_tInt32** inp = &o->In1P;
  pwr_tInt32 result = INT_MAX;
  for (i = 0; i < IMIN_SIZE; i++) {
    if (**inp < result)
      result = **inp;
    inp = (pwr_tInt32**)((char*)inp + pwr_cInputOffset);
  }
  o->ActVal = result;
}

ISel


void ISel_exec(plc_sThread* tp, pwr_sClass_ISel* o)
{
  o->Control = *o->ControlP;
  if (o->Control)
    o->ActVal = *o->In1P;
  else
    o->ActVal = *o->In2P;
}

ILimit


void ILimit_exec(plc_sThread* tp, pwr_sClass_ILimit* o)
{
  o->Max = *o->MaxP;
  o->Min = *o->MinP;
  o->In = *o->InP;
  if (o->In > o->Max) {
    o->ActVal = o->Max;
    o->High = TRUE;
    o->Low = FALSE;
  } else if (o->In < o->Min) {
    o->Low = TRUE;
    if (o->Min <= o->Max) {
      o->ActVal = o->Min;
      o->High = FALSE;
    } else {
      o->ActVal = o->Max;
      o->High = TRUE;
    }
  } else {
    o->ActVal = o->In;
    o->High = FALSE;
    o->Low = FALSE;
  }
}

IMux


void IMux_exec(plc_sThread* tp, pwr_sClass_IMux* o)
{
#define IMUX_SIZE 24
  int idx;
  pwr_tInt32** inp = &o->In0P;
  idx = o->Index = *o->IndexP;
  idx = idx < 0 ? 0 : (idx > IMUX_SIZE - 1 ? IMUX_SIZE - 1 : idx);
  inp = (pwr_tInt32**)((char*)inp + idx * pwr_cInputOffset);
  o->ActVal = **inp;
}

Mux


void Mux_exec(plc_sThread* tp, pwr_sClass_Mux* o)
{
#define MUX_SIZE 24
  int idx;
  pwr_tFloat32** inp = &o->In0P;
  idx = o->Index = *o->IndexP;
  idx = idx < 0 ? 0 : (idx > MUX_SIZE - 1 ? MUX_SIZE - 1 : idx);
  inp = (pwr_tFloat32**)((char*)inp + idx * pwr_cInputOffset);
  o->ActVal = **inp;
}

Demux


void Demux_exec(plc_sThread* tp, pwr_sClass_Demux* o)
{
#define DEMUX_SIZE 24
  int idx, i;
  pwr_tFloat32* outp = &o->Out0;
  idx = o->Index = *o->IndexP;
  for (i = 0; i < DEMUX_SIZE; i++) {
    if (i == idx)
      *outp = *o->InP;
    else
      *outp = 0;
    outp++;
  }
}

IDemux


void IDemux_exec(plc_sThread* tp, pwr_sClass_IDemux* o)
{
#define IDEMUX_SIZE 24
  int idx, i;
  pwr_tInt32* outp = &o->Out0;
  idx = o->Index = *o->IndexP;
  for (i = 0; i < IDEMUX_SIZE; i++) {
    if (i == idx)
      *outp = *o->InP;
    else
      *outp = 0;
    outp++;
  }
}

Add


void Add_exec(plc_sThread* tp, pwr_sClass_Add* o)
{
#define ADD_SIZE 8
  int i;
  pwr_tFloat32** inp = &o->In1P;
  pwr_tFloat32 sum = 0;
  for (i = 0; i < ADD_SIZE; i++) {
    sum += **inp;
    inp = (pwr_tFloat32**)((char*)inp + pwr_cInputOffset);
  }
  o->ActVal = sum;
}

Mul


void Mul_exec(plc_sThread* tp, pwr_sClass_Mul* o)
{
#define MUL_SIZE 8
  int i;
  pwr_tFloat32** inp = &o->In1P;
  pwr_tFloat32 result = **inp;
  for (i = 1; i < MUL_SIZE; i++) {
    inp = (pwr_tFloat32**)((char*)inp + pwr_cInputOffset);
    result *= **inp;
  }
  o->ActVal = result;
}

Sub


void Sub_exec(plc_sThread* tp, pwr_sClass_Sub* o)
{
  o->ActVal = *o->In1P - *o->In2P;
}

Div


void Div_exec(plc_sThread* tp, pwr_sClass_Div* o)
{
  o->ActVal = *o->In1P / *o->In2P;
}

Max


void Max_exec(plc_sThread* tp, pwr_sClass_Max* o)
{
#define AMAX_SIZE 8
  int i;
  pwr_tFloat32** inp = &o->In1P;
  pwr_tFloat32 result = -1E37;
  for (i = 0; i < AMAX_SIZE; i++) {
    if (**inp > result)
      result = **inp;
    inp = (pwr_tFloat32**)((char*)inp + pwr_cInputOffset);
  }
  o->ActVal = result;
}

Min


void Min_exec(plc_sThread* tp, pwr_sClass_Min* o)
{
#define AMIN_SIZE 8
  int i;
  pwr_tFloat32** inp = &o->In1P;
  pwr_tFloat32 result = 1E37;
  for (i = 0; i < AMIN_SIZE; i++) {
    if (**inp < result)
      result = **inp;
    inp = (pwr_tFloat32**)((char*)inp + pwr_cInputOffset);
  }
  o->ActVal = result;
}

Random


void Random_exec(plc_sThread* tp, pwr_sClass_Random* o)
{
  if ( o->CondP == &o->Cond)
    o->ActVal = o->MinValue + (float)(rand())/RAND_MAX * (o->MaxValue - o->MinValue);
  else {
    if ( *o->CondP && !o->CondOld)
      o->ActVal = o->MinValue + (float)(rand())/RAND_MAX * (o->MaxValue - o->MinValue);
    o->CondOld = *o->CondP;
  }
}

BwShiftLeft


void BwShiftLeft_exec(plc_sThread* tp, pwr_sClass_BwShiftLeft* o)
{
  o->Out = ((unsigned int)*o->InP) << (*o->NumP);
}

BwShiftRight


void BwShiftRight_exec(plc_sThread* tp, pwr_sClass_BwShiftRight* o)
{
  o->Out = ((unsigned int)*o->InP) >> (*o->NumP);
}

BwRotateRight


void BwRotateRight_exec(plc_sThread* tp, pwr_sClass_BwRotateRight* o)
{
  o->Out = ((unsigned int)(*o->InP) << (32 - *o->NumP))
      | ((unsigned int)(*o->InP) >> (*o->NumP));
}

BwRotateLeft


void BwRotateLeft_exec(plc_sThread* tp, pwr_sClass_BwRotateLeft* o)
{
  o->Out = ((unsigned int)(*o->InP) >> (32 - *o->NumP))
      | ((unsigned int)(*o->InP) << (*o->NumP));
}

AtSel


void AtSel_exec(plc_sThread* tp, pwr_sClass_AtSel* o)
{
  o->Control = *o->ControlP;
  if (o->Control)
    o->ActVal = *o->In1P;
  else
    o->ActVal = *o->In2P;
}

DtSel


void DtSel_exec(plc_sThread* tp, pwr_sClass_DtSel* o)
{
  o->Control = *o->ControlP;
  if (o->Control)
    o->ActVal = *o->In1P;
  else
    o->ActVal = *o->In2P;
}

AtMux


void AtMux_exec(plc_sThread* tp, pwr_sClass_AtMux* o)
{
#define ATMUX_SIZE 24
  int idx;
  pwr_tTime** inp = &o->In0P;
  idx = o->Index = *o->IndexP;
  idx = idx < 0 ? 0 : (idx > ATMUX_SIZE - 1 ? ATMUX_SIZE - 1 : idx);
  inp = (pwr_tTime**)((char*)inp + idx * pwr_cInputOffsetAt);
  o->ActVal = **inp;
}

DtMux


void DtMux_exec(plc_sThread* tp, pwr_sClass_DtMux* o)
{
#define DTMUX_SIZE 24
  int idx;
  pwr_tDeltaTime** inp = &o->In0P;
  idx = o->Index = *o->IndexP;
  idx = idx < 0 ? 0 : (idx > DTMUX_SIZE - 1 ? DTMUX_SIZE - 1 : idx);
  inp = (pwr_tDeltaTime**)((char*)inp + idx * pwr_cInputOffsetDt);
  o->ActVal = **inp;
}

AtMax


void AtMax_exec(plc_sThread* tp, pwr_sClass_AtMax* o)
{
#define ATMAX_SIZE 8
  int i;
  pwr_tTime** inp = &o->In1P;
  pwr_tTime result = PWR_ATTIME_MIN;
  for (i = 0; i < ATMAX_SIZE; i++) {
    if (time_Acomp_NE(*inp, &result) == 1)
      result = **inp;
    inp = (pwr_tTime**)((char*)inp + pwr_cInputOffsetAt);
  }
  o->ActVal = result;
}

AtMin


void AtMin_exec(plc_sThread* tp, pwr_sClass_AtMin* o)
{
#define ATMIN_SIZE 8
  int i;
  pwr_tTime** inp = &o->In1P;
  pwr_tTime result = PWR_ATTIME_MAX;
  for (i = 0; i < ATMIN_SIZE; i++) {
    if (time_Acomp_NE(*inp, &result) == -1)
      result = **inp;
    inp = (pwr_tTime**)((char*)inp + pwr_cInputOffsetAt);
  }
  o->ActVal = result;
}

DtMax


void DtMax_exec(plc_sThread* tp, pwr_sClass_DtMax* o)
{
#define DTMAX_SIZE 8
  int i;
  pwr_tDeltaTime** inp = &o->In1P;
  pwr_tDeltaTime result = PWR_DTTIME_MIN;
  for (i = 0; i < DTMAX_SIZE; i++) {
    if (time_Dcomp_NE(*inp, &result) == 1)
      result = **inp;
    inp = (pwr_tDeltaTime**)((char*)inp + pwr_cInputOffsetDt);
  }
  o->ActVal = result;
}

DtMin


void DtMin_exec(plc_sThread* tp, pwr_sClass_DtMin* o)
{
#define DTMIN_SIZE 8
  int i;
  pwr_tDeltaTime** inp = &o->In1P;
  pwr_tDeltaTime result = PWR_DTTIME_MAX;
  for (i = 0; i < DTMIN_SIZE; i++) {
    if (time_Dcomp_NE(*inp, &result) == -1)
      result = **inp;
    inp = (pwr_tDeltaTime**)((char*)inp + pwr_cInputOffsetDt);
  }
  o->ActVal = result;
}

AtLimit


void AtLimit_exec(plc_sThread* tp, pwr_sClass_AtLimit* o)
{
  o->Max = *o->MaxP;
  o->Min = *o->MinP;
  o->In = *o->InP;
  if (time_Acomp_NE(&o->In, &o->Max) == 1) {
    o->ActVal = o->Max;
    o->High = TRUE;
    o->Low = FALSE;
  } else if (time_Acomp_NE(&o->In, &o->Min) == -1) {
    o->Low = TRUE;
    if (time_Acomp_NE(&o->Min, &o->Max) <= 0) {
      o->ActVal = o->Min;
      o->High = FALSE;
    } else {
      o->ActVal = o->Max;
      o->High = TRUE;
    }
  } else {
    o->ActVal = o->In;
    o->High = FALSE;
    o->Low = FALSE;
  }
}

DtLimit


void DtLimit_exec(plc_sThread* tp, pwr_sClass_DtLimit* o)
{
  o->Max = *o->MaxP;
  o->Min = *o->MinP;
  o->In = *o->InP;
  if (time_Dcomp_NE(&o->In, &o->Max) == 1) {
    o->ActVal = o->Max;
    o->High = TRUE;
    o->Low = FALSE;
  } else if (time_Dcomp_NE(&o->In, &o->Min) == -1) {
    o->Low = TRUE;
    if (time_Dcomp_NE(&o->Min, &o->Max) <= 0) {
      o->ActVal = o->Min;
      o->High = FALSE;
    } else {
      o->ActVal = o->Max;
      o->High = TRUE;
    }
  } else {
    o->ActVal = o->In;
    o->High = FALSE;
    o->Low = FALSE;
  }
}

AtDemux


void AtDemux_exec(plc_sThread* tp, pwr_sClass_AtDemux* o)
{
#define ATDEMUX_SIZE 24
  int idx, i;
  pwr_tTime* outp = &o->Out0;
  idx = o->Index = *o->IndexP;
  for (i = 0; i < ATDEMUX_SIZE; i++) {
    if (i == idx)
      *outp = *o->InP;
    else
      *outp = pwr_cNTime;
    outp++;
  }
}

DtDemux


void DtDemux_exec(plc_sThread* tp, pwr_sClass_DtDemux* o)
{
  int idx, i;
  pwr_tDeltaTime* outp = &o->Out0;
  idx = o->Index = *o->IndexP;
  for (i = 0; i < ATDEMUX_SIZE; i++) {
    if (i == idx)
      *outp = *o->InP;
    else
      *outp = pwr_cNDeltaTime;
    outp++;
  }
}

StrSel


void StrSel_exec(plc_sThread* tp, pwr_sClass_StrSel* o)
{
  o->Control = *o->ControlP;
  if (o->Control)
    strncpy(o->ActVal, *o->In1P, sizeof(o->ActVal));
  else
    strncpy(o->ActVal, *o->In2P, sizeof(o->ActVal));
}

StrMux


void StrMux_exec(plc_sThread* tp, pwr_sClass_StrMux* o)
{
#define STRMUX_SIZE 24
  int idx;
  pwr_tString80** inp = &o->In0P;
  idx = o->Index = *o->IndexP;
  idx = idx < 0 ? 0 : (idx > STRMUX_SIZE - 1 ? STRMUX_SIZE - 1 : idx);
  inp = (pwr_tString80**)((char*)inp + idx * pwr_cInputOffsetStr);
  strncpy(o->ActVal, **inp, sizeof(o->ActVal));
}

StrEqual


void StrEqual_exec(plc_sThread* tp, pwr_sClass_StrEqual* o)
{
  if (o->CaseSensitive)
    o->Status = (streq(*o->In1P, *o->In2P));
  else
    o->Status = (str_NoCaseStrcmp(*o->In1P, *o->In2P) == 0);
}

StrNotEqual


void StrNotEqual_exec(plc_sThread* tp, pwr_sClass_StrNotEqual* o)
{
  if (o->CaseSensitive)
    o->Status = (!streq(*o->In1P, *o->In2P));
  else
    o->Status = (str_NoCaseStrcmp(*o->In1P, *o->In2P) != 0);
}

StrAdd


void StrAdd_exec(plc_sThread* tp, pwr_sClass_StrAdd* o)
{
#define STRADD_SIZE 8
  int i;
  pwr_tString80** inp = &o->In1P;
  pwr_tString80 sum = "";
  for (i = 0; i < STRADD_SIZE; i++) {
    strncat(sum, **inp, sizeof(sum) - strlen(sum) - 1);
    inp = (pwr_tString80**)((char*)inp + pwr_cInputOffsetStr);
  }
  strncpy(o->ActVal, sum, sizeof(o->ActVal));
  o->ActVal[sizeof(o->ActVal) - 1] = 0;
}

StrTrim


void StrTrim_exec(plc_sThread* tp, pwr_sClass_StrTrim* o)
{
  str_trim(o->ActVal, *o->InP);
}

StrParse


void StrParse_exec(plc_sThread* tp, pwr_sClass_StrParse* o)
{
#define STRPARSE_SIZE 10
  int i, tokens;
  tokens = dcli_parse(*o->InP, o->Delimiter, "", o->Token1, STRPARSE_SIZE,
      sizeof(o->Token1), 1);
  for (i = tokens; i < STRPARSE_SIZE; i++)
    *(char*)((char*)o->Token1 + i * sizeof(o->Token1)) = 0;
}