using IOB_UT_NEXT;
using IOB_UT_NEXT.Config;
using MapoSDK;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace IOB_WIN_SIEMENS.IobSiemens
{
    /// <summary>
    /// Controllo Siemens specifico x impianti Lasco (Pressa bilancere Valvital)
    /// </summary>
    public class SiemensLasco : Siemens
    {
        /* --------------------------------------------------------------------------------
         * Controlli SIEMENS LASCO (Pressa principale in VALVITAL)
         * - basasto su SIEMENS
         * - S7 vers 1500
         *
         * STRUTTURA MEMORIA DB1001 READ: 68 byte lettura, DB1002 68 byte byte scrittura, vedere doc allegato
         * F:\Drive condivisi\30_Clienti\Valvital\Comunicazione PLC\22 - LASCO Bilancere
         *

         * -------------------------------------------------------------------------------- */

        #region Public Constructors

        /// <summary>
        /// Classe base con i metodi x Siemens
        /// </summary>
        /// <param name="caller">Form chiamante</param>
        /// <param name="IOBConf">Configurazione (legacy)</param>
        /// <param name="IobConfFull">Configurazione (v 4.x)</param>
        public SiemensLasco(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
        {
            lgInfo("NEW IOB SIEMENS versione LASCO");
            lastPLCWatchDog = DateTime.Now.AddMinutes(-1);
            useNewSend = string.IsNullOrWhiteSpace(getOptPar("USE_NEW_EXE_TASK")) ? false : true;
            // imposto i parametri speciali x calcolo...
            var chiaviTSVC = findOptPar("TSVC");
            if (chiaviTSVC.Count > 0)
            {
                lgInfo($"Trovate {chiaviTSVC.Count} chiavi TSVC");
                string[] codVal;
                VCData currConf;
                int periodo = 0;
                VC_func funz = VC_func.POINT;
                // accodo nella conf...
                foreach (var item in chiaviTSVC)
                {
                    codVal = item.Value.Split(':');
                    Enum.TryParse(codVal[0], out funz);
                    int.TryParse(codVal[1], out periodo);
                    currConf = new VCData()
                    {
                        Funzione = funz,
                        Period = periodo,
                        DTStart = DateTime.Now.AddHours(-1),
                        dataArray = new List<double>()
                    };
                    // cerco se esiste eda ggiongo o aggiorno...
                    string currKey = item.Key.Replace("TSVC_", "");
                    if (TSVC_Data.ContainsKey(currKey))
                    {
                        TSVC_Data[currKey] = currConf;
                    }
                    else
                    {
                        TSVC_Data.Add(currKey, currConf);
                    }
                }
                // documento...
                foreach (var item in TSVC_Data)
                {
                    lgTrace($"TSVC: {item.Key} | periodo: {item.Value.Period} | funz: {item.Value.Funzione}");
                    // salvo i valori PREC...
                    if (LastTSVC.ContainsKey(item.Key))
                    {
                        LastTSVC[item.Key] = 0;
                    }
                    else
                    {
                        LastTSVC.Add(item.Key, 0);
                    }
                }
            }
        }

        #endregion Public Constructors

        #region Public Methods

        /// <summary>
        /// Processo i task richiesti e li elimino dalla coda 1:1
        /// </summary>
        /// <param name="task2exe"></param>
        public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe, string codTav)
        {
            // Verificare il protocollo: dovrebeb togliere SOLO i task eseguiti...
            Dictionary<string, string> taskDone = new Dictionary<string, string>();
            bool taskOk = false;
            string taskVal = "";
            string memAddrWrite = "";
            if (task2exe != null)
            {
                // inizio VUOTO
                byte[] MemBlock = new byte[parametri.memSizeWrite];

                // controllo su OPT_PAR se usare nuovo metodo exe task...
                if (useNewSend)
                {
                    // cerco task specifici
                    foreach (var item in task2exe)
                    {
                        taskOk = false;
                        taskVal = "";
                        int valore = 0;
                        // converto richiesta in enum...
                        taskType tName = taskType.nihil;
                        Enum.TryParse(item.Key, out tName);
                        // controllo sulla KEY
                        switch (tName)
                        {
                            case taskType.nihil:
                            case taskType.forceSetPzCount:
                                taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
                                break;

                            case taskType.setArt:
                            case taskType.setComm:
                            case taskType.setPzComm:
                            case taskType.setProg:
                                upsertKey(item.Key, item.Value);
                                // verifico se posso aggiornare valori in memoria...
                                if (memMap != null && memMap.mMapWrite != null)
                                {
                                    if (memMap.mMapWrite.ContainsKey(item.Key))
                                    {
                                        dataConf currMem = memMap.mMapWrite[item.Key];
                                        string addr = currMem.memAddr;
                                        taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
                                        // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
                                        memMap.mMapWrite[item.Key].value = item.Value;
                                    }
                                    else
                                    {
                                        taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}";
                                    }
                                }
                                else
                                {
                                    taskVal = $"NO MemMap found, SET task: {item.Key} --> {item.Value}";
                                }
                                int byteSize = 0;
                                // recupero dati da memMap... altrimenti NULLA
                                if (memMap.mMapWrite.ContainsKey(item.Key))
                                {
                                    dataConf currMem = memMap.mMapWrite[item.Key];
                                    byteSize = currMem.size;
                                    memAddrWrite = currMem.memAddr;
                                    MemBlock = new byte[byteSize];
                                    if (currMem.tipoMem == plcDataType.String)
                                    {
                                        saveStringOnMemBlock(ref MemBlock, item.Key, 0, byteSize);
                                    }
                                    else if (currMem.tipoMem == plcDataType.DInt)
                                    {
                                        int valDInt = 0;
                                        int.TryParse(item.Value, out valDInt);
                                        MemBlock = S7.Net.Types.DInt.ToByteArray(valDInt);
                                    }
                                    else if (currMem.tipoMem == plcDataType.Int)
                                    {
                                        short valDInt = 0;
                                        short.TryParse(item.Value, out valDInt);
                                        MemBlock = S7.Net.Types.Int.ToByteArray(valDInt);
                                    }
                                }
                                taskVal = item.Value;
                                break;

                            case taskType.sendWatchDogMes2Plc:
                                // processo scrittura BIT su DB1002.DBB0.0
                                MemBlock = new byte[1];
                                memAddrWrite = "DB1002.DBB0";
                                // compogo in byte... primo bit è setup/run, ultimo è watchdog
                                valore = inSetup ? 1 : 0;
                                // verifica SE aggiungere il veto reset contapezzi
                                if (forcePzReset)
                                {
                                    // reset shift counter
                                    valore += 2;
                                    // reset type counter
                                    valore += 4;
                                    // controlla se resettare il pz reset...
                                    forcePzReset = forcePzResetUntil >= DateTime.Now;
                                }
                                valore += (byte)(counterMes2Plc << 7);
                                MemBlock[0] = (byte)valore;
                                taskVal = $"VALUE DB1002.92 --> {valore} | counter interno {counterMes2Plc}";
                                break;

                            case taskType.forceResetPzCount:
                                // forzo reset contapezzi
                                forcePzReset = true;
                                forcePzResetUntil = DateTime.Now.AddSeconds(3);
                                break;

                            case taskType.setParameter:
                                // richiedo da URL i parametri WRITE da popolare
                                lgInfo("Chiamata processMemWriteRequests");
                                taskVal = processMemWriteRequests();
                                // se restituiscce "" faccio altra prova...
                                if (string.IsNullOrEmpty(taskVal))
                                {
                                    // i parametri me li aspetto come stringa composta paramName|paramvalue
                                    if (item.Value.Contains("|"))
                                    {
                                        string[] paramsJob = item.Value.Split('|');
                                        taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}";
                                    }
                                    else
                                    {
                                        taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value";
                                    }
                                }
                                break;

                            case taskType.startSetup:
                                // salvo che SONO IN SETUP!
                                inSetup = true;
                                lgInfo("!!! Richiesta startSetup - BLOCCO contapezzi LASCO !!!");
                                // forzo reset contapezzi
                                forcePzReset = true;
                                forcePzResetUntil = DateTime.Now.AddSeconds(3);
                                break;

                            case taskType.stopSetup:
                                // salvo che SONO FUORI DAL SETUP!
                                inSetup = false;
                                lgInfo("!!! Richiesta stopSetup - SBLOCCO contapezzi LASCO!!!");
                                // forzo reset contapezzi
                                forcePzReset = true;
                                forcePzResetUntil = DateTime.Now.AddSeconds(3);
                                break;

                            case taskType.fixStopSetup:
                                // salvo che SONO FUORI DAL SETUP!
                                inSetup = false;
                                break;

                            default:
                                taskVal = "SKIPPED | NO EXEC";
                                break;
                        }
                        // aggiungo task!
                        taskDone.Add(item.Key, taskVal);
                        // scrivo comunque!
                        taskOk = S7WriteBB(ref MemBlock, memAddrWrite);
                        if (!taskOk)
                        {
                            lgError($"Errore in S7WriteBB durante executeTasks: {item.Key} | {item.Value}");
                        }
                    }
                }
                else
                {
                    lgInfo("Use old send");
                    // cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4
                    foreach (var item in task2exe)
                    {
                        taskOk = false;
                        taskVal = "";
                        // converto richiesta in enum...
                        taskType tName = taskType.nihil;
                        Enum.TryParse(item.Key, out tName);
                        // controllo sulla KEY
                        switch (tName)
                        {
                            case taskType.nihil:
                            case taskType.forceResetPzCount:
                            case taskType.forceSetPzCount:
                            case taskType.startSetup:
                            case taskType.stopSetup:
                                taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
                                break;

                            case taskType.setArt:
                            case taskType.setComm:
                            case taskType.setPzComm:
                            case taskType.setProg:
                                //saveProdData(item);
                                upsertKey(item.Key, item.Value);
                                taskVal = item.Value;
                                break;

                            case taskType.sendWatchDogMes2Plc:
                                // compogo in byte... primo bit è setup/run, ultimo è watchdog
                                int valore = inSetup ? 1 : 0;
                                valore += (byte)(counterMes2Plc << 7);
                                MemBlock[0] = (byte)valore;
                                taskVal = $"VALUE DB1002.92 --> {counterMes2Plc}";
                                break;

                            case taskType.setParameter:
                                // richiedo da URL i parametri WRITE da popolare
                                lgInfo("Chiamata processMemWriteRequests");
                                taskVal = processMemWriteRequests();
                                // se restituiscce "" faccio altra prova...
                                if (string.IsNullOrEmpty(taskVal))
                                {
                                    // i parametri me li aspetto come stringa composta paramName|paramvalue
                                    if (item.Value.Contains("|"))
                                    {
                                        string[] paramsJob = item.Value.Split('|');
                                        taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}";
                                    }
                                    else
                                    {
                                        taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value";
                                    }
                                }
                                break;

                            case taskType.fixStopSetup:
                                // salvo che SONO FUORI DAL SETUP!
                                inSetup = false;
                                break;

                            default:
                                taskVal = "SKIPPED | NO EXEC";
                                break;
                        }
                        // aggiungo task!
                        taskDone.Add(item.Key, taskVal);
                    }
                    // controllo SE HO da scrivere articolo/commessa/programma
                    saveStringOnMemBlock(ref MemBlock, "setArt", 2, 22);
                    saveStringOnMemBlock(ref MemBlock, "setComm", 24, 22);
                    saveStringOnMemBlock(ref MemBlock, "setProg", 46, 22);

                    // scrivo comunque!
                    taskOk = S7WriteBB(ref MemBlock);
                }
            }
            return taskDone;
        }

        /// <summary>
        /// Recupero dati dinamici in formato dictionary
        /// </summary>
        /// <returns></returns>
        public override Dictionary<string, string> getDynData()
        {
            bool disableDynData = true;
            Dictionary<string, string> outVal = new Dictionary<string, string>();
            // processing
            try
            {
                disableDynData = (getOptPar("DISABLE_DYN_DATA") == "TRUE");
            }
            catch (Exception exc)
            {
                lgError(exc, "Errore in getOptPar x DISABLE_DYN_DATA");
            }
            try
            {
                // le 3 posizioni sono in 1/10 mm x cui vanno divise x 10... x avere MM
                double RamPosition = S7.Net.Types.Int.FromByteArray(RawInput.Skip(18).Take(2).ToArray()) / 10;
                double LowerEjectorPosition = S7.Net.Types.Int.FromByteArray(RawInput.Skip(20).Take(2).ToArray()) / 10;
                double UpperTool = S7.Net.Types.Int.FromByteArray(RawInput.Skip(22).Take(2).ToArray()) / 10;
                // temp in °C
                int TempMainMotorU = S7.Net.Types.Int.FromByteArray(RawInput.Skip(24).Take(2).ToArray());
                int TempMainMotorV = S7.Net.Types.Int.FromByteArray(RawInput.Skip(26).Take(2).ToArray());
                int TempMainMotorW = S7.Net.Types.Int.FromByteArray(RawInput.Skip(28).Take(2).ToArray());
                int TempSpindleNut = S7.Net.Types.Int.FromByteArray(RawInput.Skip(30).Take(2).ToArray());
                int TempMotoModule = S7.Net.Types.Int.FromByteArray(RawInput.Skip(32).Take(2).ToArray());
                int TempOilCirculation = S7.Net.Types.Int.FromByteArray(RawInput.Skip(34).Take(2).ToArray());
                int TempHydraulicUnit = S7.Net.Types.Int.FromByteArray(RawInput.Skip(36).Take(2).ToArray());
                // press in BAR
                int PressHydraulicPump = S7.Net.Types.Int.FromByteArray(RawInput.Skip(38).Take(2).ToArray());
                int PressHydraulicAccumulator = S7.Net.Types.Int.FromByteArray(RawInput.Skip(40).Take(2).ToArray());
                int PressCounterforceOil = S7.Net.Types.Int.FromByteArray(RawInput.Skip(42).Take(2).ToArray());
                int PressCounterforceGas = S7.Net.Types.Int.FromByteArray(RawInput.Skip(44).Take(2).ToArray());
                // forze in kN (10^3 Newton)
                int ForcePressureActual = S7.Net.Types.DInt.FromByteArray(RawInput.Skip(46).Take(4).ToArray());
                int ForceOnBushing = S7.Net.Types.Int.FromByteArray(RawInput.Skip(50).Take(2).ToArray());

                if (utils.CRB("enableTSVC"))
                {
                    // per sicurezza prendo 1 sec avanti così da prendere tutti i valori insieme
                    DateTime adesso = DateTime.Now.AddSeconds(1);
                    bool[] scaduti = new bool[16];
                    // salvo in stack le VC rilevate
                    scaduti[0] = stackVal_TSVC("RamPosition", RamPosition, adesso);
                    scaduti[1] = stackVal_TSVC("LowerEjectorPosition", LowerEjectorPosition, adesso);
                    scaduti[2] = stackVal_TSVC("UpperTool", UpperTool, adesso);
                    scaduti[3] = stackVal_TSVC("TempMainMotorU", TempMainMotorU, adesso);
                    scaduti[4] = stackVal_TSVC("TempMainMotorV", TempMainMotorV, adesso);
                    scaduti[5] = stackVal_TSVC("TempMainMotorW", TempMainMotorW, adesso);
                    scaduti[6] = stackVal_TSVC("TempSpindleNut", TempSpindleNut, adesso);
                    scaduti[7] = stackVal_TSVC("TempMotoModule", TempMotoModule, adesso);
                    scaduti[8] = stackVal_TSVC("TempOilCirculation", TempOilCirculation, adesso);
                    scaduti[9] = stackVal_TSVC("TempHydraulicUnit", TempHydraulicUnit, adesso);
                    scaduti[10] = stackVal_TSVC("PressHydraulicPump", PressHydraulicPump, adesso);
                    scaduti[11] = stackVal_TSVC("PressHydraulicAccumulator", PressHydraulicAccumulator, adesso);
                    scaduti[12] = stackVal_TSVC("PressCounterforceOil", PressCounterforceOil, adesso);
                    scaduti[13] = stackVal_TSVC("PressCounterforceGas", PressCounterforceGas, adesso);
                    scaduti[14] = stackVal_TSVC("ForcePressureActual", ForcePressureActual, adesso);
                    scaduti[15] = stackVal_TSVC("ForceOnBushing", ForceOnBushing, adesso);

                    // verifico SE devo riportare dati VC
                    if (baseUtils.CountTrue(scaduti) > 0)
                    {
                        RamPosition = (int)getVal_TSVC("RamPosition", scaduti[0]);
                        LowerEjectorPosition = (int)getVal_TSVC("LowerEjectorPosition", scaduti[1]);
                        UpperTool = (int)getVal_TSVC("UpperTool", scaduti[2]);
                        TempMainMotorU = (int)getVal_TSVC("TempMainMotorU", scaduti[3]);
                        TempMainMotorV = (int)getVal_TSVC("TempMainMotorV", scaduti[4]);
                        TempMainMotorW = (int)getVal_TSVC("TempMainMotorW", scaduti[5]);
                        TempSpindleNut = (int)getVal_TSVC("TempSpindleNut", scaduti[6]);
                        TempMotoModule = (int)getVal_TSVC("TempMotoModule", scaduti[7]);
                        TempOilCirculation = (int)getVal_TSVC("TempOilCirculation", scaduti[8]);
                        TempHydraulicUnit = (int)getVal_TSVC("TempHydraulicUnit", scaduti[9]);
                        PressHydraulicPump = (int)getVal_TSVC("PressHydraulicPump", scaduti[10]);
                        PressHydraulicAccumulator = (int)getVal_TSVC("PressHydraulicAccumulator", scaduti[11]);
                        PressCounterforceOil = (int)getVal_TSVC("PressCounterforceOil", scaduti[12]);
                        PressCounterforceGas = (int)getVal_TSVC("PressCounterforceGas", scaduti[13]);
                        ForcePressureActual = (int)getVal_TSVC("ForcePressureActual", scaduti[14]);
                        ForceOnBushing = (int)getVal_TSVC("ForceOnBushing", scaduti[15]);

                        //vecchia gestione valori INT
                        //RamPosition = getVal_TSVC_int("RamPosition", scaduti[0]);
                        //LowerEjectorPosition = getVal_TSVC_int("LowerEjectorPosition", scaduti[1]);
                        //UpperTool = getVal_TSVC_int("UpperTool", scaduti[2]);
                        //TempMainMotorU = getVal_TSVC_int("TempMainMotorU", scaduti[3]);
                        //TempMainMotorV = getVal_TSVC_int("TempMainMotorV", scaduti[4]);
                        //TempMainMotorW = getVal_TSVC_int("TempMainMotorW", scaduti[5]);
                        //TempSpindleNut = getVal_TSVC_int("TempSpindleNut", scaduti[6]);
                        //TempMotoModule = getVal_TSVC_int("TempMotoModule", scaduti[7]);
                        //TempOilCirculation = getVal_TSVC_int("TempOilCirculation", scaduti[8]);
                        //TempHydraulicUnit = getVal_TSVC_int("TempHydraulicUnit", scaduti[9]);
                        //PressHydraulicPump = getVal_TSVC_int("PressHydraulicPump", scaduti[10]);
                        //PressHydraulicAccumulator = getVal_TSVC_int("PressHydraulicAccumulator", scaduti[11]);
                        //PressCounterforceOil = getVal_TSVC_int("PressCounterforceOil", scaduti[12]);
                        //PressCounterforceGas = getVal_TSVC_int("PressCounterforceGas", scaduti[13]);
                        //ForcePressureActual = getVal_TSVC_int("ForcePressureActual", scaduti[14]);
                        //ForceOnBushing = getVal_TSVC_int("ForceOnBushing", scaduti[15]);

                        outVal.Add("RamPosition", $"{RamPosition:N1}");
                        outVal.Add("LowerEjectorPosition", $"{LowerEjectorPosition:N1}");
                        outVal.Add("UpperTool", $"{UpperTool:N1}");
                        outVal.Add("TempMainMotorU", $"{TempMainMotorU}");
                        outVal.Add("TempMainMotorV", $"{TempMainMotorV}");
                        outVal.Add("TempMainMotorW", $"{TempMainMotorW}");
                        outVal.Add("TempSpindleNut", $"{TempSpindleNut}");
                        outVal.Add("TempMotoModule", $"{TempMotoModule}");
                        outVal.Add("TempOilCirculation", $"{TempOilCirculation}");
                        outVal.Add("TempHydraulicUnit", $"{TempHydraulicUnit}");
                        outVal.Add("PressHydraulicPump", $"{PressHydraulicPump}");
                        outVal.Add("PressHydraulicAccumulator", $"{PressHydraulicAccumulator}");
                        outVal.Add("PressCounterforceOil", $"{PressCounterforceOil}");
                        outVal.Add("PressCounterforceGas", $"{PressCounterforceGas}");
                        outVal.Add("ForcePressureActual", $"{ForcePressureActual}");
                        outVal.Add("ForceOnBushing", $"{ForceOnBushing}");

                        // salvo!
                        LastTSVC["RamPosition"] = RamPosition;
                        LastTSVC["LowerEjectorPosition"] = LowerEjectorPosition;
                        LastTSVC["UpperTool"] = UpperTool;
                        LastTSVC["TempMainMotorU"] = TempMainMotorU;
                        LastTSVC["TempMainMotorV"] = TempMainMotorV;
                        LastTSVC["TempMainMotorW"] = TempMainMotorW;
                        LastTSVC["TempSpindleNut"] = TempSpindleNut;
                        LastTSVC["TempMotoModule"] = TempMotoModule;
                        LastTSVC["TempOilCirculation"] = TempOilCirculation;
                        LastTSVC["TempHydraulicUnit"] = TempHydraulicUnit;
                        LastTSVC["PressHydraulicPump"] = PressHydraulicPump;
                        LastTSVC["PressHydraulicAccumulator"] = PressHydraulicAccumulator;
                        LastTSVC["PressCounterforceOil"] = PressCounterforceOil;
                        LastTSVC["PressCounterforceGas"] = PressCounterforceGas;
                        LastTSVC["ForcePressureActual"] = ForcePressureActual;
                        LastTSVC["ForceOnBushing"] = ForceOnBushing;
                    }
                    else
                    {
                        // se i dynData non sono disabilitati...
                        if (!disableDynData)
                        {
                            outVal.Add("DYNDATA", $"RamPosition {LastTSVC["RamPosition"]:N1} | ForcePressureActual {LastTSVC["ForcePressureActual"]} | ForceOnBushing {LastTSVC["ForceOnBushing"]}");
                        }
                    }
                }
                else
                {
                    // se i dynData non sono disabilitati...
                    if (!disableDynData)
                    {
                        outVal.Add("DYNDATA", $"RamPosition {LastTSVC["RamPosition"]:N1} | ForcePressureActual {LastTSVC["ForcePressureActual"]} | ForceOnBushing {LastTSVC["ForceOnBushing"]}");
                    }
                }
            }
            catch (Exception exc)
            {
                lgError(exc, "Errore in getDynData x Siemens LASCO");
            }
            return outVal;
        }

        public override string getPrgName()
        {
            string answ = "";
            try
            {
                string rawProdCode = S7.Net.Types.String.FromByteArray(RawInput.Skip(54).Take(12).ToArray());
                // primi due char sono \f e \n avanzamento carta e nuova linea...
                answ = Regex.Replace(rawProdCode, @"\t|\n|\r|\f", "");
            }
            catch (Exception exc)
            {
                lgError($"Errore in decodifica product code{Environment.NewLine}{exc}");
            }
            return answ;
        }

        /// <summary>
        /// Effettua processing del recupero delle OVERRIDE (spindle, feedrate, rapid)
        /// </summary>
        public override void processOverride()
        {
        }

        public override void processWhatchDog()
        {
            // scrivo 1 volta al secondo il contatore incrementale su area apposita scrive nel primo
            // byte, ultimo bit, il watchdog, mentre scrive nel primo 1 = setup, 0 = run
            DateTime adesso = DateTime.Now;
            if (adesso.Subtract(lastPLCWatchDog).TotalSeconds > watchDogPeriod)
            {
                // incremento
                counterMes2Plc++;
                // se > 1 --> 0 (balla solo 0..1)
                if (counterMes2Plc > 1) counterMes2Plc = 0;
                // salvo su DB
                Dictionary<string, string> task2exe = new Dictionary<string, string>();
                Dictionary<string, string> taskDone = new Dictionary<string, string>();
                task2exe.Add("sendWatchDogMes2Plc", counterMes2Plc.ToString());
                taskDone = executeTasks(task2exe, "");
                // salvo watchdog PLC
                lastPLCWatchDog = adesso;
            }
        }

        #endregion Public Methods

        #region Protected Fields

        protected int counterMes2Plc = 0;

        protected int counterPlc2Mes = 0;

        protected int counterPlc2MesWrote = 0;

        protected DateTime lastPLCWatchDog;

        protected bool useNewSend = false;

        #endregion Protected Fields

        #region Protected Methods

        /// <summary>
        /// Effettua decodifica aree memoria alla bitmap usata x MAPO
        /// </summary>
        protected override void decodeToBaseBitmap()
        {
            // init a zero...
            B_input = 0;

            /* -----------------------------------------------------
             * bitmap MAPO STANDARD
             * B0: POWER_ON
             * B1: RUN
             * B2: pzCount
             * B3: allarme
             * B4: manuale
             * B5: emergenza
             *
             *
             * - BIT di stato
             *   - DBX0.0: RUN STATE
             *   - DBX0.1: CYCLE
             *   - DBX0.2: MANUAL MODE
             *   - DBX0.3: GENERAL ALARM
             *   - DBX0.4: ESTOP (1=OK, 0 = emergency)
             *   - DBX0.5: SAFETY DOORS
             ----------------------------------------------------- */

            byte mainData = RawInput[0];

            int byteSignals = 0;
            // bit 0 (poweron) imposto a 1 SE connected...
            if (currPLC.IsConnected)
            {
                byteSignals += (1 << 0);
            }
            if ((mainData & (1 << 0)) == 1)
            {
                byteSignals += (1 << 1);
            }

            // EMERGENZA
            if ((mainData & (1 << 5)) == 1)
            {
                byteSignals += (1 << 5);
            }
            // ALLARME
            if ((mainData & (1 << 3)) == 1)
            {
                byteSignals += (1 << 3);
            }

            // MANUALE ...
            if ((mainData & (1 << 2)) == 1)
            {
                byteSignals += (1 << 4);
            }

            // salvo!
            B_input = byteSignals;

            // log opzionale!
            if (verboseLog)
            {
                lgInfo(string.Format($"Trasformazione dati: RawInput:{RawInput[3]} --> B_input: {B_input}"));
            }
        }

        #endregion Protected Methods
    }
}