/*
 * 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 <string.h>
#include "co_math.h"
#include "rt_plc.h"
#include "rt_plc_io.h"

PiPos


void pipos_exec(plc_sThread* tp, pwr_sClass_pipos* object)
{
  float pdiff; /* Position difference from Kal1 */
  int idiff; /* Raw value difference from Kal1 */
  /* Read Input */
  object->PulsIn = *object->PulsInP;
  object->CalPos1 = *object->CalPos1P;
  object->CalOrder1 = *object->CalOrder1P;
  object->CalPos2 = *object->CalPos2P;
  object->CalOrder2 = *object->CalOrder2P;
  /* Calibration point 1 */
  if (object->CalOrder1 && !object->CalOrder1Old) {
    object->PosCal1 = object->ActVal = object->CalPos1;
    object->PICal1 = object->PulsIn;
    /* Clear order if manual */
    if (object->CalOrder1P == &object->CalOrder1)
      object->CalOrder1 = FALSE;
  } else if (object->CalOrder2 && !object->CalOrder2Old) {
    /*	Calibration point 2 */
    pdiff = object->CalPos2 - object->PosCal1;
    idiff = object->PulsIn - object->PICal1;
    if (!feqf(pdiff, 0.0f) && idiff != 0) {
      object->Gain = pdiff / idiff;
      object->PosCal2 = object->ActVal = object->CalPos2;
    }
    /* Clear order if manual */
    if (object->CalOrder2P == &object->CalOrder2)
      object->CalOrder2 = FALSE;
  } else {
    /* Calculate position when no calibration */
    idiff = object->PulsIn - object->PICal1;
    object->ActVal = object->PosCal1 + object->Gain * idiff;
  }
  /* Save old values */
  object->CalOrder1Old = object->CalOrder1;
  object->CalOrder2Old = object->CalOrder2;
}

Count


void count_exec(plc_sThread* tp, pwr_sClass_count* object)
{
  float old; /* For flank detect */
  /* Count up */
  old = object->CountUp;
  object->CountUp = *object->CountUpP;
  if (object->CountUp && !old)
    object->Accum++;
  /* Count down	*/
  old = object->CountDown;
  object->CountDown = *object->CountDownP;
  if (object->CountDown && !old)
    object->Accum--;
  /* Initialize	*/
  old = object->Init;
  object->Init = *object->InitP;
  if (object->Init && !old)
    object->Accum = object->Preset;
  /* Clear */
  old = object->Clear;
  object->Clear = *object->ClearP;
  if (object->Clear && !old)
    object->Accum = 0;
  /* Set Output status */
  if (object->Accum > 0) {
    object->Pos = TRUE;
    object->Neg = FALSE;
    object->Zero = FALSE;
  } else if (object->Accum == 0) {
    object->Pos = FALSE;
    object->Neg = FALSE;
    object->Zero = TRUE;
  } else {
    object->Pos = FALSE;
    object->Neg = TRUE;
    object->Zero = FALSE;
  }
  object->Equal = object->Accum == object->Preset;
}

BCDDO


void bcddo_exec(plc_sThread* tp, pwr_sClass_bcddo* object)
{
  int val; /* One digit */
  int i;
  int j;
  int tal;
  int oldtal;
  float rest; /* Overflow */
  pwr_tBoolean* dopoint; /* Points to output */
  rest = object->In = *object->InP; /* Get Input */
  dopoint = &object->BCD0; /* Initialize pointer */
  if (rest < 0)
    rest = 0;
  tal = rest / 10000;
  tal = rest - tal * 10000;
  rest = (rest - tal) / 10000;
  for (i = 0; i < 4; i++) { /* Loop 4 digits */
    oldtal = tal;
    tal = tal / 10;
    val = oldtal - 10 * tal; /* Integer 0 - 9 */
    for (j = 0; j < 4; j++) {
      *dopoint++ = (pwr_tBoolean)(val & 1);
      val = val / 2;
    }
  }
  object->Rest = rest;
}

DIBCD


#define DIBCDSIZE 16
void dibcd_exec(plc_sThread* tp, pwr_sClass_dibcd* object)
{
  int val; /* One digit */
  int i; /* Loop index*/
  int j; /* Loop index*/
  int res; /* Result */
  char* ptr; /* Pointer to pointer */
  pwr_tBoolean* p2; /* Pointer to digin */
  pwr_tBoolean err; /* Error flag */
  /* Initialize */
  res = 0;
  err = FALSE;
  ptr = (char*)&object->BCD0P + (DIBCDSIZE - 1) * pwr_cInputOffset;
  /* Double loop for convert */
  for (i = 0; i < (DIBCDSIZE / 4); i++) {
    val = 0;
    for (j = 0; j < 4; j++) {
      val += val; /* Mult 2 */
      p2 = *(pwr_tBoolean**)ptr; /* Pointer to input */
      if (*p2 != object->Inv)
        val++; /* Signal till ? */
      ptr -= pwr_cInputOffset; /* Pointer to next pointer */
    }
    if (val > 9)
      err = TRUE;
    res = 10 * res + val;
  }
  if (!err)
    object->ActVal = res;
  object->Error = err;
}

Gray


#define GRAYSIZE 16
void gray_exec(plc_sThread* tp, pwr_sClass_gray* object)
{
  int i; /* Loopcounter */
  pwr_tBoolean in; /* Digital in after invert */
  pwr_tBoolean odd; /* Convert flag */
  int sum; /* Convert sum */
  char* ptr; /* Pointer to ptr to digin */
  pwr_tBoolean* p2; /* Pointer to digin */
  /* Init */
  odd = 0;
  sum = 0;
  ptr = (char*)&object->Din0P + (GRAYSIZE - 1) * pwr_cInputOffset;
  /* Graycode convert loop */
  for (i = 0; i < GRAYSIZE; i++) {
    sum += sum; /* Mult 2 */
    p2 = *(pwr_tBoolean**)ptr; /* Pointer to next dig in */
    in = (*p2 != object->Inv); /* Invert ? */
    odd = in ? !odd : odd; /* Odd up to now ? */
    sum += odd; /* Inc if odd input */
    ptr -= pwr_cInputOffset;
  }
  /* Result */
  object->ActVal = sum;
}

GETDPPTR


void GetDpPtr_init(pwr_sClass_GetDpPtr* o)
{
  pwr_tUInt32 p;
  pwr_tAttrRef aref = o->DpPtrObject;
  /* Reset the indirect bit to fetch the pointer, not the value */
  aref.Flags.b.Indirect = 0;
  if (ODD(gdh_GetObjectInfoAttrref(&aref, &p, sizeof(p))))
    o->Ptr = gdh_TranslateRtdbPointer(p);
}
void GetDpPtr_exec(plc_sThread* tp, pwr_sClass_GetDpPtr* o)
{
  if (o->Ptr)
    o->Value = *o->Ptr;
}

GETAPPTR


void GetApPtr_init(pwr_sClass_GetApPtr* o)
{
  pwr_tUInt32 p;
  pwr_tAttrRef aref = o->ApPtrObject;
  /* Reset the indirect bit to fetch the pointer, not the value */
  aref.Flags.b.Indirect = 0;
  if (ODD(gdh_GetObjectInfoAttrref(&aref, &p, sizeof(p))))
    o->Ptr = gdh_TranslateRtdbPointer(p);
}
void GetApPtr_exec(plc_sThread* tp, pwr_sClass_GetApPtr* o)
{
  if (o->Ptr)
    o->Value = *o->Ptr;
}

GETIPPTR


void GetIpPtr_init(pwr_sClass_GetIpPtr* o)
{
  pwr_tUInt32 p;
  pwr_tAttrRef aref = o->IpPtrObject;
  /* Reset the indirect bit to fetch the pointer, not the value */
  aref.Flags.b.Indirect = 0;
  if (ODD(gdh_GetObjectInfoAttrref(&aref, &p, sizeof(p))))
    o->Ptr = gdh_TranslateRtdbPointer(p);
}
void GetIpPtr_exec(plc_sThread* tp, pwr_sClass_GetIpPtr* o)
{
  if (o->Ptr)
    o->Value = *o->Ptr;
}

STODPPTR


void StoDpPtr_init(pwr_sClass_StoDpPtr* o)
{
  pwr_tUInt32 p;
  pwr_tAttrRef aref = o->DpPtrObject;
  /* Reset the indirect bit to fetch the pointer, not the value */
  aref.Flags.b.Indirect = 0;
  if (ODD(gdh_GetObjectInfoAttrref(&aref, &p, sizeof(p))))
    o->Ptr = gdh_TranslateRtdbPointer(p);
}
void StoDpPtr_exec(plc_sThread* tp, pwr_sClass_StoDpPtr* o)
{
  if (o->Ptr)
    *o->Ptr = *o->InP;
}

STOAPPTR


void StoApPtr_init(pwr_sClass_StoApPtr* o)
{
  pwr_tUInt32 p;
  pwr_tAttrRef aref = o->ApPtrObject;
  /* Reset the indirect bit to fetch the pointer, not the value */
  aref.Flags.b.Indirect = 0;
  if (ODD(gdh_GetObjectInfoAttrref(&aref, &p, sizeof(p))))
    o->Ptr = gdh_TranslateRtdbPointer(p);
}
void StoApPtr_exec(plc_sThread* tp, pwr_sClass_StoApPtr* o)
{
  if (o->Ptr)
    *o->Ptr = *o->InP;
}

STOIPPTR


void StoIpPtr_init(pwr_sClass_StoIpPtr* o)
{
  pwr_tUInt32 p;
  pwr_tAttrRef aref = o->IpPtrObject;
  /* Reset the indirect bit to fetch the pointer, not the value */
  aref.Flags.b.Indirect = 0;
  if (ODD(gdh_GetObjectInfoAttrref(&aref, &p, sizeof(p))))
    o->Ptr = gdh_TranslateRtdbPointer(p);
}
void StoIpPtr_exec(plc_sThread* tp, pwr_sClass_StoIpPtr* o)
{
  if (o->Ptr)
    *o->Ptr = *o->InP;
}

ENUMTOSTR


void EnumToStr_init(pwr_sClass_EnumToStr* o)
{
  if (EVEN(gdh_GetEnumValueDef(
          o->TypeId, (gdh_sValueDef**)&o->EnumDefP, (int*)&o->EnumDefRows)))
    o->EnumDefP = 0;
}
void EnumToStr_exec(plc_sThread* tp, pwr_sClass_EnumToStr* o)
{
  int i;
  int found = 0;
  if (!o->EnumDefP)
    return;
  for (i = 0; i < o->EnumDefRows; i++) {
    if (((gdh_sValueDef*)o->EnumDefP)[i].Value->Value == *o->InP) {
      strncpy(o->ActVal, ((gdh_sValueDef*)o->EnumDefP)[i].Value->Text,
          sizeof(o->ActVal));
      found = 1;
      break;
    }
  }
  if (!found)
    strcpy(o->ActVal, "");
}

STRTOENUM


void StrToEnum_init(pwr_sClass_StrToEnum* o)
{
  if (EVEN(gdh_GetEnumValueDef(
          o->TypeId, (gdh_sValueDef**)&o->EnumDefP, (int*)&o->EnumDefRows)))
    o->EnumDefP = 0;
}
void StrToEnum_exec(plc_sThread* tp, pwr_sClass_StrToEnum* o)
{
  int i;
  int found = 0;
  if (!o->EnumDefP)
    return;
  for (i = 0; i < o->EnumDefRows; i++) {
    if (strcmp((char*)o->StrP, ((gdh_sValueDef*)o->EnumDefP)[i].Value->Text)
        == 0) {
      o->ActVal = ((gdh_sValueDef*)o->EnumDefP)[i].Value->Value;
      found = 1;
      break;
    }
  }
  if (!found)
    o->ActVal = 0;
}