using EgwProxy.MultiCncLib.App.Native;
using EgwProxy.MultiCncLib.CNC;
using IOB_UT_NEXT;
using IOB_UT_NEXT.Config;
using MapoSDK;
using NLog;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Security.Policy;
using System.Threading;
using System.Threading.Tasks;
using static EgwProxy.MultiCncLib.CNC.FANUC;

namespace IOB_WIN_FANUC.Iob
{
    public class Fanuc : Iob.GenericNext
    {
        #region Public Constructors

        /// <summary>
        /// estende l'init della classe base...
        /// </summary>
        /// <param name="caller">Form chiamante</param>
        /// <param name="IobConfFull">Configurazione (v 4.x)</param>
        public Fanuc(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
        {
            lgInfo("Start init Fanuc");
            // i dati RAW principali sono 6 byte...
            RawInput = new byte[6];

            // gestione invio ritardato contapezzi

            lastPzCountSend = DateTime.Now;
            lastWarnODL = DateTime.Now;
            int gSize = 0;
            int rSize = 8;
            int xSize = 8;
            int ySize = 8;
            if (IOBConfFull.Special.BankConf.AreaConf.ContainsKey("AREAG"))
            {
                gSize = IOBConfFull.Special.BankConf.AreaConf["AREAG"].Size;
            }
            if (IOBConfFull.Special.BankConf.AreaConf.ContainsKey("AREAR"))
            {
                rSize = IOBConfFull.Special.BankConf.AreaConf["AREAR"].Size;
            }
            if (IOBConfFull.Special.BankConf.AreaConf.ContainsKey("AREAX"))
            {
                xSize = IOBConfFull.Special.BankConf.AreaConf["AREAX"].Size;
            }
            if (IOBConfFull.Special.BankConf.AreaConf.ContainsKey("AREAY"))
            {
                ySize = IOBConfFull.Special.BankConf.AreaConf["AREAY"].Size;
            }
            MemBlockG = new byte[gSize];
            MemBlockR = new byte[rSize];
            MemBlockX = new byte[xSize];
            MemBlockY = new byte[ySize];

            // forzo letture single threaded x FANUC!!!
            IOBConfFull.General.MachWLoopSingleThread = true;
            // x FANUC riduco num errori std x forzare riavvio rapido
            maxErroriCheck = maxErroriCheck > 15 ? 15 : maxErroriCheck;

            // loggo aree di memoria avviate...
            lgInfo($"Init area di memoria | MemBlockG: {gSize}b | MemBlockR: {rSize}b  | MemBlockX: {xSize}b  | MemBlockY: {ySize}b ");

            areaD = FormatAreaFanuc("AreaD", "AREAD");
            areaPAR = FormatAreaFanuc("AreaPARR", "PAR");
            areaR = FormatAreaFanuc("AreaR", "AREAR");
            areaX = FormatAreaFanuc("AreaX", "AREAX");
            areaY = FormatAreaFanuc("AreaY", "AREAY");

            lgInfo($"Set Mem | {areaD.areaName}: {areaD.startIdx} +{areaD.arraySize} | {areaR.areaName}: {areaR.startIdx} +{areaR.arraySize} | {areaX.areaName}: {areaX.startIdx} +{areaX.arraySize} | {areaY.areaName}: {areaY.startIdx} +{areaY.arraySize}");

            lgInfoStartup($"Start init Adapter FANUC all'IP {IOBConfFull.Device.Connect.IpAddr}:{IOBConfFull.Device.Connect.Port} per IOB {IOBConfFull.General.CodIOB}");

            // Creo oggetto connessione NC
            parentForm.commPlcActive = true;
            Runtime.CreateNC(CNC.NcType.FANUC, IOBConfFull.Device.Connect.IpAddr, IOBConfFull.Device.Connect.Port);
            parentForm.commPlcActive = false;

            // aggiungo referenza obj FANUC
            FANUC_ref = (FANUC)Runtime.NC;

            tryDisconnect();
            Thread.Sleep(500);

            lgInfo("FANUC: tryConnect");
            tryConnect();
            if (enablePzCountByApp)
            {
                lgDebug("FANUC: inizio gestione contapezzi");
                try
                {
                    // verifico quale modalità sia richiesta: STD (6711) oppure BIT (Custom, con
                    // indicazione area)
                    if (!string.IsNullOrEmpty(IOBConfFull.Device.PzCountMode))
                    {
                        if (IOBConfFull.Device.PzCountMode.StartsWith("STD"))
                        {
                            lgInfo("Init contapezzi FANUC: pzCntReload(true)");
                            pzCntReload(true);
                            // refresh associazione Macchina - IOB
                            SendM2IOB();
                            // per adesso imposto lettura fanuc == contapezzi (poi farà vera lettura...)
                            contapezziPLC = contapezziIOB;
                        }
                        else
                        {
                            contapezziIOB = 0;
                            lgInfo($"Contapezzi STD disabilitato: modalità {IOBConfFull.Device.PzCountMode}");
                        }
                    }
                    else
                    {
                        contapezziIOB = 0;
                        lgInfo("Parametro mancante PzCountMode");
                    }
                }
                catch (Exception exc)
                {
                    lgError(exc, "Errore in contapezzi FANUC 01");
                }
            }
            // finisco INIT ADAPTER
            lgInfo("End init Adapter FANUC");
        }

        #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: dovrebbe togliere SOLO i task eseguiti...
            Dictionary<string, string> taskDone = new Dictionary<string, string>();
            if (task2exe != null)
            {
                bool taskOk = false;
                string taskVal = "";
                string newVal = "";
                string memAddr = "";
                int newValInt = 0;
                // 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);
                    switch (tName)
                    {
                        case taskType.setArt:
                        case taskType.setComm:
                            lgInfo($"Richiesta scrittura {tName}");
                            // salvo in memoria il valore richiesto...
                            if (memMap != null && memMap.mMapWrite != null)
                            {
                                if (memMap.mMapWrite.ContainsKey(item.Key))
                                {
                                    dataConf currMem = memMap.mMapWrite[item.Key];
                                    memAddr = currMem.memAddr;
                                    taskVal = $"SET task: {item.Key} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
                                    // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
                                    memMap.mMapWrite[item.Key].value = newVal;
                                }
                                else
                                {
                                    taskVal = $"NO DATA MEM, SET task: {item.Key} --> {newVal} ({item.Value})";
                                }
                            }
                            else
                            {
                                taskVal = $"NO BankConf found, SET task: {item.Key} --> {newVal} ({item.Value})";
                            }
                            upsertKey(item.Key, item.Value);
                            break;

                        case taskType.nihil:
                        case taskType.fixStopSetup:
                        case taskType.setProg:
                        case taskType.sendWatchDogMes2Plc:
                            taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
                            lgTrace($"sendWatchDogMes2Plc | Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
                            break;

                        case taskType.setArtNum:
                            lgInfo($"Richiesta scrittura CodART numerico");
                            // in primis faccio una chiamata per tutta la tab SE fosse vuoto il dict
                            // di traduzione
                            if (DictNumArt == null || DictNumArt.Count == 0)
                            {
                                getNumArt("");
                                lgInfo($"Recuperato DictNumArt, trovati {DictNumArt.Count} rec");
                            }
                            // chiamo server x avere decodifica valore INT
                            newVal = getNumArt(item.Value);
                            // se c'è gestione semplificata num articolo --> usa trim dei soli
                            // caratteri alfabetici lasciando numeri..
                            if (numArtCharTrim)
                            {
                                newVal = baseUtils.GetNumbers(item.Value);
                            }
                            // converto int...
                            int.TryParse(newVal, out newValInt);
                            // procedo come il resto cercando mappatura in memMap: recupero dati da memMap...
                            if (memMap != null && memMap.mMapWrite != null)
                            {
                                if (memMap.mMapWrite.ContainsKey(item.Key))
                                {
                                    dataConf currMem = memMap.mMapWrite[item.Key];
                                    memAddr = currMem.memAddr;
                                    taskVal = $"SET task: {item.Key} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
                                    // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
                                    memMap.mMapWrite[item.Key].value = newVal;
                                }
                                else
                                {
                                    taskVal = $"NO DATA MEM, SET task: {item.Key} --> {newVal} ({item.Value})";
                                }
                            }
                            else
                            {
                                taskVal = $"NO BankConf found, SET task: {item.Key} --> {newVal} ({item.Value})";
                            }

                            // invio a controller...
                            taskOk = setNumArt(newValInt);
                            //se ho successo rileggo ed eventualmente sistemo valore
                            if (taskOk)
                            {
                                //var rVal = getValByParam("SET_NUM_ART");
                                var rVal = getValByMemAddr(memAddr);
                                if (rVal == newVal)
                                {
                                    lgInfo("All OK");
                                    // invio aggiornamento x reset richiesta... test!
                                    _ = sendOptVal(item.Key, newVal);
                                }
                                else
                                {
                                    lgError($"setArtNum | Error | memAddr: {memAddr} | rVal: {rVal} | newVal: {newVal}");
                                }
                            }
                            else
                            {
                                lgError("setArtNum | Errore in scrittura SetNumArt");
                            }
                            // salvo in currProd..
                            upsertKey(item.Key, item.Value);
                            break;

                        case taskType.setCommNum:
                            lgInfo($"Richiesta scrittura cod COMMESSA numerica");
                            // chiamo server x avere decodifica valore INT
                            newVal = getNumComm(item.Value);
                            // converto int...
                            int.TryParse(newVal, out newValInt);
                            //riscrivo senza eventuali zeri...
                            newVal = newValInt > 0 ? $"{newValInt}" : newVal;
                            lgTrace($"Conv.Int numComm | newVal: {newVal} | newValInt: {newValInt}");
                            // procedo come il resto cercando mappatura in memMap: recupero dati da memMap...
                            if (memMap != null && memMap.mMapWrite != null)
                            {
                                if (memMap.mMapWrite.ContainsKey(item.Key))
                                {
                                    dataConf currMem = memMap.mMapWrite[item.Key];
                                    memAddr = currMem.memAddr;
                                    taskVal = $"SET task: {item.Key} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
                                    // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
                                    memMap.mMapWrite[item.Key].value = newVal;
                                }
                                else
                                {
                                    taskVal = $"NO DATA MEM, SET task: {item.Key} --> {newVal} ({item.Value})";
                                }
                            }
                            else
                            {
                                taskVal = $"NO BankConf found, SET task: {item.Key} --> {newVal} ({item.Value})";
                            }

                            // invio a controller...
                            taskOk = setNumCom(newValInt);
                            //se ho successo rileggo ed eventualmente sistemo valore
                            if (taskOk)
                            {
                                //var rVal = getValByParam("SET_NUM_COM");
                                var rVal = getValByMemAddr(memAddr);
                                if (rVal == newVal)
                                {
                                    lgInfo("All OK");
                                    // invio aggiornamento x reset richiesta... test!
                                    _ = sendOptVal(item.Key, newVal);
                                }
                                else
                                {
                                    lgError($"setCommNum | Error | memAddr: {memAddr} | rVal: {rVal} | newVal: {newVal}");
                                }
                            }
                            else
                            {
                                lgError("setCommNum | Errore in scrittura SetNumCom");
                            }
                            // salvo in currProd..
                            upsertKey(item.Key, item.Value);
                            break;

                        case taskType.forceResetPzCount:
                            // set pezzi zero inizio setup
                            taskOk = setPzCnt(0);
                            taskVal = taskOk ? "forceResetPzCount: OK" : "forceResetPzCount | NO EXEC";
                            lgInfo($"Chiamata forceResetPzCount: taskOk: {taskOk} | taskVal: {taskVal}");

                            // salvo in currProd..
                            upsertKey(item.Key, item.Value);

                            break;

                        case taskType.forceSetPzCount:
                            int pzCnt = 0;
                            int.TryParse(item.Value, out pzCnt);
                            // set pezzi richiesti inizio setup
                            taskOk = setPzCnt(pzCnt);
                            taskVal = taskOk ? "forceSetPzCount: OK" : "forceSetPzCount | NO EXEC";
                            lgInfo($"Chiamata forceSetPzCount: taskOk: {taskOk} | taskVal: {taskVal}");

                            // salvo in currProd..
                            upsertKey(item.Key, item.Value);

                            break;

                        case taskType.setPzComm:
                            int pzReq = 0;
                            int.TryParse(item.Value, out pzReq);
                            // set pezzi richiesti inizio setup
                            taskOk = setPzComm(pzReq);
                            taskVal = taskOk ? "setPzComm | RESET DONE: SETUP START" : "setPzComm | PZ RESET DISABLED | NO EXEC";
                            lgInfo($"Chiamata startSetup: taskOk: {taskOk} | taskVal: {taskVal}");

                            // salvo in currProd..
                            upsertKey(item.Key, item.Value);
                            break;

                        case taskType.startSetup:
                            // reset contapezzi inizio setup
                            if (IOBConfFull.Counters.ResetOnSetupStart)
                            {
                                taskOk = resetContapezziPLC(codTav);
                            }
                            taskVal = taskOk ? "startSetup | RESET: SETUP START" : "startSetup | PZ RESET DISABLED | NO EXEC";
                            lgInfo($"Chiamata startSetup: taskOk: {taskOk} | taskVal: {taskVal}");
                            break;

                        case taskType.stopSetup:
                            // reset contapezzi fine setup SE ESPLICITAMENTE IMPOSTATO
                            if (IOBConfFull.Counters.ResetOnSetupStop)
                            {
                                taskOk = resetContapezziPLC(codTav);
                            }
                            taskVal = taskOk ? "stopSetup | RESET: SETUP END" : "stopSetup | PZ RESET DISABLED | NO EXEC";
                            lgInfo($"Chiamata stopSetup: taskOk: {taskOk} | taskVal: {taskVal}");
                            break;

                        case taskType.endProd:
                            // reset contapezzi inizio setup
                            if (IOBConfFull.Counters.ResetOnProdEnd)
                            {
                                lgDebug($"Fanuc.executeTasks {tName} | resetContapezziPLC");
                                taskOk = resetContapezziPLC(codTav);
                            }
                            break;

                        case taskType.setParameter:
                            lgInfo($"setParameter | NO processing: taskOk: {taskOk} | taskVal: {taskVal}");
                            taskVal = "setParameter | SKIPPED | NO EXEC";
                            break;

                        default:
                            lgInfo($"executeTasks | default senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
                            taskVal = "SKIPPED | NO EXEC";
                            break;
                    }
                    // aggiungo task!
                    taskDone.Add(item.Key, taskVal);
                }
            }
            return taskDone;
        }

        /// <summary>
        /// Wrapper chiamata lettura/scrittura INTERO in area macro...
        /// </summary>
        /// <param name="bWrite"></param>
        /// <param name="memIndex"></param>
        /// <param name="Value"></param>
        /// <returns></returns>
        public bool FanucMemMacroRW(bool bWrite, Int32 memIndex, ref short Value)
        {
            bool answ = false;
            string topic = $"FanucMemMacroRW.short.{memIndex}";
            if (connectionOk)
            {
                if (FANUC_ref.Connected)
                {
                    try
                    {
                        parentForm.commPlcActive = true;
                        // provo ad usare SEMPRE double...
                        short zeroVal = 0;
                        // in primis scrivo reset a zero...
                        answ = FANUC_ref.F_RW_Macro_Short(bWrite, memIndex, ref zeroVal);
                        // ora scrivo vero valore...
                        answ = FANUC_ref.F_RW_Macro_Short(bWrite, memIndex, ref Value);
                        tryReduceErrorCount(topic);
                    }
                    catch (Exception exc)
                    {
                        tryIncreaseErrorComm(topic);
                        lgError($"Eccezione in FanucMemMacroRW | Short | MemType: MACRO | memIndex: {memIndex} | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
                    }
                }
                else
                {
                    connectionOk = false;
                }
            }
            parentForm.commPlcActive = false;
            return answ;
        }

        /// <summary>
        /// Wrapper chiamata lettura/scrittura DOUBLE in area macro...
        /// </summary>
        /// <param name="bWrite"></param>
        /// <param name="memIndex"></param>
        /// <param name="Value"></param>
        /// <returns></returns>
        public bool FanucMemMacroRW(bool bWrite, Int32 memIndex, ref double Value)
        {
            bool answ = false;
            string topic = $"FanucMemMacroRW.double.{memIndex}";
            if (connectionOk)
            {
                if (FANUC_ref.Connected)
                {
                    try
                    {
                        parentForm.commPlcActive = true;
                        short zeroVal = 0;
                        // in primis scrivo reset a zero...
                        answ = FANUC_ref.F_RW_Macro_Short(bWrite, memIndex, ref zeroVal);
                        // ora scrivo vero valore...
                        answ = FANUC_ref.F_RW_Macro_Double(bWrite, memIndex, ref Value);
                        tryReduceErrorCount(topic);
                    }
                    catch (Exception exc)
                    {
                        tryIncreaseErrorComm(topic);
                        lgError($"Eccezione in FanucMemMacroRW | Double | MemType: MACRO | memIndex: {memIndex} | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
                    }
                }
                else
                {
                    connectionOk = false;
                }
            }
            parentForm.commPlcActive = false;
            return answ;
        }

        /// <summary>
        /// wrapper chiamata lettura/scrittura SINGOLO BYTE...
        /// </summary>
        /// <param name="bWrite"></param>
        /// <param name="MemType"></param>
        /// <param name="memIndex"></param>
        /// <param name="Value"></param>
        /// <returns></returns>
        public bool FanucMemRW(bool bWrite, FANUC.MemType MemType, Int32 memIndex, ref byte Value)
        {
            bool answ = false;
            string topic = $"FanucMemRW.{MemType}.byte.{memIndex}";
            if (connectionOk)
            {
                if (FANUC_ref.Connected)
                {
                    try
                    {
                        parentForm.commPlcActive = true;
                        answ = FANUC_ref.F_RW_Byte(bWrite, MemType, memIndex, ref Value);
                        tryReduceErrorCount(topic);
                    }
                    catch (Exception exc)
                    {
                        tryIncreaseErrorComm(topic);
                        lgError($"Eccezione in FanucMemRW | Byte | MemType: {MemType} | memIndex: {memIndex} | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
                    }
                }
                else
                {
                    connectionOk = false;
                }
            }
            parentForm.commPlcActive = false;
            return answ;
        }

        /// <summary>
        /// wrapper chiamata lettura/scrittura MULTI BYTE...
        /// </summary>
        /// <param name="bWrite"></param>
        /// <param name="MemType"></param>
        /// <param name="memIndex"></param>
        /// <param name="Value"></param>
        /// <returns></returns>
        public bool FanucMemRW(bool bWrite, FANUC.MemType MemType, Int32 memIndex, ref byte[] Value)
        {
            bool answ = false;
            string topic = $"FanucMemRW.{MemType}.byte_array.{memIndex}";
            if (connectionOk)
            {
                if (FANUC_ref.Connected)
                {
                    try
                    {
                        parentForm.commPlcActive = true;
                        answ = FANUC_ref.F_RW_Byte(bWrite, MemType, memIndex, ref Value);
                        tryReduceErrorCount(topic);
                    }
                    catch (Exception exc)
                    {
                        tryIncreaseErrorComm(topic);
                        lgError($"Eccezione in FanucMemRW | MultiByte | MemType: {MemType} | memIndex: {memIndex} | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
                    }
                }
                else
                {
                    connectionOk = false;
                }
            }
            parentForm.commPlcActive = false;
            return answ;
        }

        /// <summary>
        /// Recupero dati dinamici...
        /// </summary>
        public override Dictionary<string, string> getDynData()
        {
            Dictionary<string, string> outVal = new Dictionary<string, string>();
            string topic = $"FanucMemRW.getDynData";
            // processo SOLO SE connected...
            if (connectionOk)
            {
                if (FANUC_ref.Connected)
                {
                    try
                    {
                        sw.Restart();
                        EgwProxy.MultiCncLib.Focas1.ODBDY2_1 answ = FANUC_ref.getAllDynData();
                        if (utils.CRB("recTime"))
                        {
                            TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("PROC-DYN-DATA"), sw.ElapsedTicks);
                        }
                        string actf = answ.actf.ToString();
                        string acts = answ.acts.ToString();
                        outVal.Add("DYNDATA", string.Format("FEED {0}#SPEED_RPM {1}", actf, acts));
                        if (utils.CRB("SendFeedSpeed"))
                        {
                            outVal.Add("FEED", actf);
                            outVal.Add("SPEED_RPM", acts);
                            //outVal.Add("NUM_ALARM", numAlarm);
                        }
                        if (utils.CRB("SendAxPos"))
                        {
                            // salvo le posizioni...
                            EgwProxy.MultiCncLib.Focas1.FAXIS posAx = answ.pos;
                            int[] currPosAbs = posAx.absolute;
                            int i = 0;
                            foreach (var item in currPosAbs)
                            {
                                i++;
                                outVal.Add(string.Format("POS_{0:00}", i), item.ToString());
                            }
                        }
                        tryReduceErrorCount(topic);
                    }
                    catch (Exception exc)
                    {
                        tryIncreaseErrorComm(topic);
                        lgError($"Errore in getDynData | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
                    }
                    sw.Stop();
                }
            }
            // se supero soglia errori lettura --> disconnetto e resetto
            if (numErroriCheck > maxErroriCheck)
            {
                lgError($"numErroriCheck: {numErroriCheck} --> disconnessione adapter con tryDisconnect");

                numErroriCheck = 0;
                connectionOk = false;
                tryDisconnect();
            }
            if (outVal.Count > 0)
            {
                tryReduceErrorCount(topic);
            }
            return outVal;
        }

        /// <summary>
        /// Recupero dati override (da area G che è già stata letta...)
        /// </summary>
        /// <returns></returns>
        public override Dictionary<string, string> getOverrides()
        {
            Dictionary<string, string> outVal = new Dictionary<string, string>();
            // processo SOLO SE connected...
            if (connectionOk)
            {
                if (FANUC_ref.Connected)
                {
                    if (utils.CRB("enableMode") && MemBlockG != null && MemBlockG.Length > 0)
                    {
                        outVal.Add("FEED_OVER", MemBlockG[30].ToString());
                        outVal.Add("RAPID_OVER", MemBlockG[12].ToString());
                    }
                }
            }
            return outVal;
        }

        /// <summary>
        /// Recupero programma in lavorazione
        /// </summary>
        /// <returns></returns>
        public override string getPrgName()
        {
            string prgName = "";
            string topic = $"FanucMemRW.getPrgName";
            DateTime adesso = DateTime.Now;
            try
            {
                // recupero nome programma MAIN
                var is30 = FANUC_ref.Is30Series;
                var rawName = FANUC_ref.getPrgNameMain();
                prgName = utils.purgedChar2String(rawName);
                // trimmo path del programma, ovvero "CNCMEMUSERPATH1"
                prgName = prgName.Replace(utils.CRS("basePrgMemPath"), "");
                lgInfo("Current PROG: {0}", prgName);
                tryReduceErrorCount(topic);
            }
            catch (Exception exc)
            {
                tryIncreaseErrorComm(topic);
                lgError($"Eccezione in recupero PRG NAME MAIN | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
            }
            // se supero soglia errori lettura --> disconnetto e resetto
            if (numErroriCheck > maxErroriCheck)
            {
                lgError($"numErroriCheck: {numErroriCheck} --> disconnessione adapter con tryDisconnect");

                numErroriCheck = 0;
                connectionOk = false;
                tryDisconnect();
            }
            return prgName;
        }

        /// <summary>
        /// Recupero programma in lavorazione come Dictionary FANUC...
        /// - SYSINFO: (prima KEY globale) TUTTI i valori separati da # (x fare check modifica)
        /// - altre stringhe: ogni singolo parametro / valore
        /// </summary>
        /// <returns></returns>
        public override Dictionary<string, string> getSysInfo()
        {
            Dictionary<string, string> outVal = new Dictionary<string, string>();
            sw.Restart();
            EgwProxy.MultiCncLib.Focas1.ODBSYS answ = FANUC_ref.getSysInfo();
            if (utils.CRB("recTime"))
            {
                TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("SYS-INFO"), sw.ElapsedTicks);
            }

            try
            {
                string cnc_type = new string(answ.cnc_type);
                string mt_type = new string(answ.mt_type);
                string series = new string(answ.series);
                string version = new string(answ.version);
                string axes = new string(answ.axes);
                //short addInfo = memName.addinfo;
                short max_axis = answ.max_axis;
                // preparo i singoli valori dell'array...
                outVal.Add("SYSINFO", string.Format("{0}#{1}#{2}#{3}#{4}#{5}", cnc_type, mt_type, series, version, axes, max_axis));
                outVal.Add("CNC", cnc_type);
                outVal.Add("MTT", mt_type);
                outVal.Add("SER", series);
                outVal.Add("VER", version);
                outVal.Add("AXS", string.Format("{0}|{1}", axes, max_axis));
            }
            catch (Exception exc)
            {
                lgError(exc, "Errore in getSysInfo");
                connectionOk = false;
            }
            return outVal;
        }

        public override async Task InitializeAsync()
        {
            // invio altri dati accessori...
            await SendMachineConfAsync();
        }

        /// <summary>
        /// Effettua vero processing contapezzi:
        /// 6711: pezzi lavorati
        /// 6712: pezzi lavorati totali
        /// 6713: pezzi richiesti
        /// </summary>
        public override void processContapezzi()
        {
            string topic = $"processContapezzi";
            if (enablePzCountByApp)
            {
                // procedo SOLO SE ho connessione...
                if (connectionOk)
                {
                    try
                    {
                        // contapezzi ATTUALE
                        if (!string.IsNullOrEmpty(IOBConfFull.Device.PzCountMode))
                        {
                            // verifico quale modalità sia richiesta: STD (6711) oppure BIT
                            // (Custom, con indicazione area)
                            string memAddr = IOBConfFull.Device.PzCountMode;
                            topic = $"processContapezzi.{memAddr}";
                            if (memAddr.StartsWith("STD"))
                            {
                                // inizio verifica area memoria/parametro levando prima parte codice
                                memAddr = memAddr.Replace("STD.", "");
                            }
                            // var di appoggio
                            int cntAddr = 0;
                            object outputVal = new object();
                            bool done = false;
                            // verifico se si tratta di lettura parametro... formato tipo STD.PAR.6711
                            if (memAddr.StartsWith("PAR."))
                            {
                                // recupero parametro...
                                int.TryParse(memAddr.Replace("PAR.", ""), out cntAddr);
                                if (cntAddr == 0)
                                {
                                    cntAddr = 6711;
                                }
                                // processo parametro contapezzi (lavorati)
                                sw.Restart();
                                done = FANUC_ref.F_RW_Param_Integer(false, cntAddr, 3, ref outputVal);
                                if (utils.CRB("recTime"))
                                {
                                    TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("R{0}-PAR", 4), sw.ElapsedTicks);
                                }
                                // salvo ultimo conteggio rilevato
                                int newVal = -1;
                                Int32.TryParse(outputVal.ToString(), out newVal);
                                contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
                            }
                            // 2022.05.23 gestione MACRO da testare (Jetco)
                            else if (memAddr.StartsWith("MACRO."))
                            {
                                lgTrace($"Decodifica memoria MACRO | memAddr: {memAddr}");
                                // recupero parametro...
                                int.TryParse(memAddr.Replace("MACRO.", ""), out cntAddr);
                                // processo parametro
                                sw.Restart();
                                double macroVal = 0;
                                done = FANUC_ref.F_Read_macro(cntAddr, ref macroVal);
                                if (utils.CRB("recTime"))
                                {
                                    TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("R{0}-MACRO", 5), sw.ElapsedTicks);
                                }
                                // salvo ultimo conteggio rilevato
                                int newVal = -1;
                                Int32.TryParse($"{macroVal}", out newVal);
                                contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
                            }
                            // altrimenti se legge da area memoria specifica leggo da li...
                            // formato tipo STD.D.1604.DW
                            else
                            {
                                memAddressFanuc areaCounter = new memAddressFanuc(memAddr);
                                if (isVerboseLog)
                                {
                                    lgInfo("processContapezzi [0] area memoria: {0}.{1}.{2}", areaCounter.mType, areaCounter.mPos, areaCounter.vType);
                                }
                                // leggo!
                                sw.Restart();
                                // switch x tipo dati --> tipo lettura... e salvo ultimo
                                // conteggio rilevato
                                switch (areaCounter.vType)
                                {
                                    case "B":
                                        byte valB = 0;
                                        done = FANUC_ref.F_RW_Byte(false, areaCounter.mType, areaCounter.mPos, ref valB);
                                        outputVal = valB;
                                        break;

                                    case "D":
                                        ushort valW = 0;
                                        done = FANUC_ref.F_RW_Word(false, areaCounter.mType, areaCounter.mPos, ref valW);
                                        outputVal = valW;
                                        break;

                                    case "DW":
                                        uint valDW = 0;
                                        done = FANUC_ref.F_RW_DWord(false, areaCounter.mType, areaCounter.mPos, ref valDW);
                                        if (isVerboseLog)
                                        {
                                            lgInfo("[1] valDW contapezzi: {0}", valDW);
                                        }

                                        outputVal = valDW;
                                        if (isVerboseLog)
                                        {
                                            lgInfo("[2] outputVal contapezzi: {0}", outputVal);
                                        }

                                        break;

                                    default:
                                        break;
                                }
                                if (utils.CRB("recTime"))
                                {
                                    TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("R-{0}.{1}.{2}", areaCounter.mType, areaCounter.mPos, areaCounter.vType), sw.ElapsedTicks);
                                }
                                // salvo...
                                int newVal = -1;
                                Int32.TryParse(outputVal.ToString(), out newVal);
                                contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
                                //if (isVerboseLog)
                                //{
                                lgInfo("[3] contapezziPLC contapezzi: {0}", contapezziPLC);
                                //}
                            }
                            sw.Stop();
                            tryReduceErrorCount(topic);
                        }
                    }
                    catch (Exception exc)
                    {
                        tryIncreaseErrorComm(topic);
                        lgError($"Eccezione in contapezzi FANUC 02 | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
                    }
                    // se supero soglia errori lettura --> disconnetto e resetto
                    if (numErroriCheck > maxErroriCheck)
                    {
                        lgError($"numErroriCheck: {numErroriCheck} --> disconnessione adapter con tryDisconnect");

                        numErroriCheck = 0;
                        connectionOk = false;
                        tryDisconnect();
                    }
                }
                else
                {
                    lgError("Errore: manca connessione in contapezzi FANUC");
                }
            }
        }

        /// <summary>
        /// Esegue processing MODE (e nel contempo recupera altri dati dell'area G)
        /// </summary>
        public override void processMode()
        {
            string topic = $"FanucMemRW.processMode.G";
            // processo SOLO SE connected...
            if (connectionOk)
            {
                if (FANUC_ref.Connected)
                {
                    if (utils.CRB("enableMode") && MemBlockG != null && MemBlockG.Length > 0)
                    {
                        bool done = false;
                        try
                        {
                            // leggo tutto da 0 a 43...
                            int memIndex = 0;
                            // controllo modalità lettura memoria
                            sw.Restart();
                            done = FanucMemRW(R, FANUC.MemType.G, memIndex, ref MemBlockG);
                            if (utils.CRB("recTime"))
                            {
                                TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("R{0}-G-AREA", MemBlockG.Length), sw.ElapsedTicks);
                            }

                            sw.Stop();
                            // verifico modo con valore corrente, se cambia aggiorno...
                            CNC_MODE newMode = decodeG43(MemBlockG[43]);
                            tryReduceErrorCount(topic);
                            if (newMode != currMode)
                            {
                                // aggiorno!
                                currMode = newMode;
                                // conversione NUM MODE in descrizione da ENUM
                                string descrMode = Enum.GetName(typeof(CNC_MODE), currMode);
                                // accodo x invio
                                string sVal = string.Format("[CNC_MODE]{0}", descrMode);
                                // chiamo accodamento...
                                bool sent = accodaFLog("CNC_MODE", sVal, qEncodeFLog("CNC_MODE", descrMode));
                                if (sent)
                                {    // traccio valore DynData x analisi
                                    trackDynData("CNC_MODE", descrMode);
                                }
                            }
                        }
                        catch (Exception exc)
                        {
                            tryIncreaseErrorComm(topic);
                            lgError($"Errore in process Mode G43 | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
                        }
                        // se supero soglia errori lettura --> disconnetto e resetto
                        if (numErroriCheck > maxErroriCheck)
                        {
                            lgError($"numErroriCheck: {numErroriCheck} --> disconnessione adapter con tryDisconnect");

                            numErroriCheck = 0;
                            connectionOk = false;
                            tryDisconnect();
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Recupero altri counters se ci sono
        /// </summary>
        public override async Task processOtherCounters()
        {
            string topic = $"FanucMemRW.processOtherCounters";
            try
            {
                // processo verifica ed invio 1:1
                await checkSendCounter("PzReq", memNamePzReq, memAddrPzReq);
                await checkSendCounter("Articolo", memNameArt, memAddrArt);
                await checkSendCounter("Commessa", memNameCom, memAddrCom);
                // solo SE abilitato contapezzi...
                if (IOBConfFull.Device.EnabPzCount)
                {
                    await checkSendCounter("PzCount", memNamePzCnt, memAddrPzCnt);
                    await checkSendCounter("FreeCounter", memNamePzCntFree, memAddrPzCntFree);
                    await checkSendCounter("PzCntTOT", memNamePzCntTot, memAddrPzCntTot);
                }
                await checkSendCounter("Cadenza", memNameCaden, memAddrCaden);
                tryReduceErrorCount(topic);
            }
            catch (Exception exc)
            {
                tryIncreaseErrorComm(topic);
                lgError($"Errore in processOtherCounters: {Environment.NewLine}{exc}");
            }
        }

        /// <summary>
        /// Effettua lettura semafori principale <paramref name="currDispData">Parametri da
        /// aggiornare x display in form</paramref>
        /// </summary>
        public override void readSemafori(ref newDisplayData currDispData)
        {
            DateTime adesso = DateTime.Now;
            base.readSemafori(ref currDispData);
            string topic = $"FanucMemRW.readSemafori";
            // verifico non sia in veto invio iniziale...
            if (queueInEnabCurr)
            {
                // se ho errori wait proporzionale...
                if (numErroriCheck > 0)
                {
                    int sleepTime = rndGen.Next(50, 100) + 10 * numErroriCheck;
                    Thread.Sleep(sleepTime);
                }
                try
                {
                    if (verboseLog)
                    {
                        lgInfo("inizio read semafori");
                    }

                    bool readOk = true;
                    currDispData.semIn = Semaforo.SV;
                    // ogni lettura inizia da SUA area inizio controllo area R: se ha dati (> 0
                    // byte) --> leggo!
                    if (MemBlockR.Length > 0)
                    {
                        sw.Restart();
                        readOk = readOk && FanucMemRW(R, FANUC.MemType.R, areaR.startIdx, ref MemBlockR);
                        if (utils.CRB("recTime"))
                        {
                            TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("R{0}-R", MemBlockR.Length), sw.ElapsedTicks);
                        }
                        // log
                        if (verboseLog)
                        {
                            for (int i = 0; i < MemBlockR.Length; i++)
                            {
                                lgInfo(string.Format("MemBlockR{0}: {1}", i, utils.binaryForm(MemBlockR[i])));
                            }
                        }
                    }
                    // controllo area X: se ha dati (> 0 byte) --> leggo!
                    if (MemBlockX.Length > 0)
                    {
                        sw.Restart();
                        readOk = readOk && FanucMemRW(R, FANUC.MemType.X, areaX.startIdx, ref MemBlockX);
                        if (utils.CRB("recTime"))
                        {
                            TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("R{0}-X", MemBlockX.Length), sw.ElapsedTicks);
                        }
                        // log
                        if (verboseLog)
                        {
                            for (int i = 0; i < MemBlockX.Length; i++)
                            {
                                lgInfo(string.Format("MemBlockX{0}: {1}", i, utils.binaryForm(MemBlockX[i])));
                            }
                        }
                    }
                    // controllo area Y: se ha dati (> 0 byte) --> leggo!
                    if (MemBlockY.Length > 0)
                    {
                        sw.Restart();
                        readOk = readOk && FanucMemRW(R, FANUC.MemType.Y, areaY.startIdx, ref MemBlockY);
                        if (utils.CRB("recTime"))
                        {
                            TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("R{0}-Y", MemBlockY.Length), sw.ElapsedTicks);
                        }
                        // log
                        if (verboseLog)
                        {
                            for (int i = 0; i < MemBlockY.Length; i++)
                            {
                                lgInfo(string.Format("MemBlockY{0}: {1}", i, utils.binaryForm(MemBlockY[i])));
                            }
                        }
                    }
                    sw.Stop();
                    // salvo il solo BYTE dell'input decifrando il semaforo...
                    decodeToBitmap();
                    reportRawInput(ref currDispData);
                    // se tutte le letture OK decremento
                    if (readOk)
                    {
                        tryReduceErrorCount(topic);
                    }
                }
                catch (Exception exc)
                {
                    tryIncreaseErrorComm(topic);
                    lgError(string.Format("Eccezione in readSemafori:{0}{1}", Environment.NewLine, exc));
                    currDispData.semIn = Semaforo.SR;
                }

                // se supero soglia errori lettura --> disconnetto e resetto
                if (numErroriCheck > maxErroriCheck)
                {
                    lgError($"numErroriCheck: {numErroriCheck} --> disconnessione adapter con tryDisconnect");

                    numErroriCheck = 0;
                    connectionOk = false;
                    tryDisconnect();
                }
            }
            else
            {
                lgDebug($"[VETO readSemafori] | veto attivo alle {adesso:yyyy.MM.dd HH:mm:ss}");
                checkVetoQueueIn();
            }
        }

        /// <summary>
        /// Effettua reset del contapezzi
        /// </summary>
        /// <returns></returns>
        public override bool resetContapezziPLC(string codTav)
        {
            bool answ = false;
            string topic = $"FanucMemRW.resetContapezziPLC";
            // scrivo valore 0 x il contapezzi
            try
            {
                // contapezzi ATTUALE
                if (!string.IsNullOrEmpty(IOBConfFull.Device.PzCountMode))
                {
                    // verifico quale modalità sia richiesta: STD (6711) oppure BIT (Custom, con
                    // indicazione area)
                    string memAddr = IOBConfFull.Device.PzCountMode;
                    topic = $"FanucMemRW.resetContapezziPLC.{memAddr}";
                    if (memAddr.StartsWith("STD"))
                    {
                        // inizio verifica area memoria/parametro levando prima parte codice
                        memAddr = memAddr.Replace("STD.", "");
                    }
                    // var di appoggio
                    int cntAddr = 0;
                    // var contapezzi a zero....
                    object newVal = new object();
                    newVal = 0;
                    // verifico se si tratta di lettura parametro... formato tipo STD.PAR.6711
                    if (memAddr.StartsWith("PAR."))
                    {
                        // recupero parametro...
                        int.TryParse(memAddr.Replace("PAR.", ""), out cntAddr);
                        if (cntAddr == 0)
                        {
                            cntAddr = 6711;
                        }

                        // processo RESET contapezzi (lavorati)
                        sw.Restart();
                        answ = FANUC_ref.F_RW_Param_Integer(true, cntAddr, 3, ref newVal);
                        if (utils.CRB("recTime"))
                        {
                            TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("W{0}-PAR", 4), sw.ElapsedTicks);
                        }
                    }
                    else if (memAddr.StartsWith("MACRO."))
                    {
                        // recupero parametro...
                        int.TryParse(memAddr.Replace("MACRO.", ""), out cntAddr);
                        if (cntAddr == 0)
                        {
                            cntAddr = 9999;
                        }
                        // processo SET contapezzi (lavorati)
                        sw.Restart();
                        double vTransf = (double)0;
                        answ = FanucMemMacroRW(true, cntAddr, ref vTransf);
                        if (utils.CRB("recTime"))
                        {
                            TimingData.addResult(IOBConfFull.General.CodIOB, "MACRO-SHORT", sw.ElapsedTicks);
                        }
                    }
                    // altrimenti se legge da area memoria specifica leggo da li... formto tipo STD.D.1604.DW
                    else
                    {
                        memAddressFanuc areaCounter = new memAddressFanuc(memAddr);

                        if (isVerboseLog)
                        {
                            lgInfo("resetContapezziPLC [0] area memoria: {0}.{1}.{2}", areaCounter.mType, areaCounter.mPos, areaCounter.vType);
                        }
                        // leggo!
                        sw.Restart();
                        // switch x tipo dati --> tipo lettura... e salvo ultimo conteggio rilevato
                        switch (areaCounter.vType)
                        {
                            case "B":
                                byte valB = 0;
                                answ = FANUC_ref.F_RW_Byte(true, areaCounter.mType, areaCounter.mPos, ref valB);
                                newVal = valB;
                                break;

                            case "D":
                                ushort valW = 0;
                                answ = FANUC_ref.F_RW_Word(true, areaCounter.mType, areaCounter.mPos, ref valW);
                                newVal = valW;
                                break;

                            case "DW":
                                uint valDW = 0;
                                answ = FANUC_ref.F_RW_DWord(true, areaCounter.mType, areaCounter.mPos, ref valDW);
                                break;

                            default:
                                break;
                        }
                        if (utils.CRB("recTime"))
                        {
                            TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("W-{0}.{1}.{2}", areaCounter.mType, areaCounter.mPos, areaCounter.vType), sw.ElapsedTicks);
                        }
                    }
                    sw.Stop();
                    answ = true;
                    tryReduceErrorCount(topic);
                }
            }
            catch (Exception exc)
            {
                tryIncreaseErrorComm(topic);
                lgError($"Errore in RESET contapezzi FANUC | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
            }
            // se supero soglia errori lettura --> disconnetto e resetto
            if (numErroriCheck > maxErroriCheck)
            {
                lgError($"numErroriCheck: {numErroriCheck} --> disconnessione adapter con tryDisconnect");

                numErroriCheck = 0;
                connectionOk = false;
                tryDisconnect();
            }

            return answ;
        }

        /// <summary>
        /// Override salvataggio valori in memoria...
        /// </summary>
        /// <param name="tipo">tipo di DUMP</param>
        public override void saveMemDump(dumpType tipo)
        {
            // se l'area ha una size > 0...
            if (areaD.arraySize > 0)
            {
                dump_MemArea(tipo, FANUC.MemType.D, areaD.startIdx, areaD.arraySize);
            }
            // se l'area ha una size > 0...
            if (areaR.arraySize > 0)
            {
                dump_MemArea(tipo, FANUC.MemType.R, areaR.startIdx, areaR.arraySize);
            }
            // se l'area ha una size > 0...
            if (areaX.arraySize > 0)
            {
                dump_MemArea(tipo, FANUC.MemType.X, areaX.startIdx, areaX.arraySize);
            }
            // se l'area ha una size > 0...
            if (areaY.arraySize > 0)
            {
                dump_MemArea(tipo, FANUC.MemType.Y, areaY.startIdx, areaY.arraySize);
            }
            // se l'area ha una size > 0...
            if (areaPAR.arraySize > 0)
            {
                dump_ParArea(tipo, areaPAR.startIdx, areaPAR.arraySize);
            }
        }

        /// <summary>
        /// Effettua impostazione del valore numerico ARTICOLO (specifico FANUC)
        /// </summary>
        /// <returns></returns>
        public bool setNumArt(int valReq)
        {
            string topic = $"FanucMemRW.setNumArt";
            bool answ = false;
            string memAddr = memAddrArt;
            lgTrace($"INIT setNumArt | memAddr: {memAddr}");
            if (!string.IsNullOrEmpty(memAddr))
            {
                // scrivo valore richiesto in area configurata
                try
                {
                    // var di appoggio
                    int cntAddr = 0;
                    // verifico se si tratta di lettura MACRO... formato tipo MACRO.6711
                    if (memAddr.StartsWith("MACRO."))
                    {
                        // recupero parametro...
                        int.TryParse(memAddr.Replace("MACRO.", ""), out cntAddr);
                        if (cntAddr == 0)
                        {
                            cntAddr = 9999;
                        }
                        lgTrace($"MACRO | ART Write 01 | memAddr: {memAddr} | idx: {cntAddr}");
                        // processo SET contapezzi (lavorati)
                        sw.Restart();
                        // comincio scrivendo reset a zero x cominciare...

                        double vTransf = (double)valReq;
                        answ = FanucMemMacroRW(true, cntAddr, ref vTransf);
                        lgTrace($"MACRO | ART Write 02 | memAddr: {memAddr} | valReq: {valReq} | vTransf: {vTransf}");
                        if (utils.CRB("recTime"))
                        {
                            TimingData.addResult(IOBConfFull.General.CodIOB, "MACRO-SHORT", sw.ElapsedTicks);
                        }
                    }
                    sw.Stop();
                    tryReduceErrorCount(topic);
                }
                catch (Exception exc)
                {
                    tryIncreaseErrorComm(topic);
                    lgError($"Errore in setNumArtFANUC | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
                }
                // se supero soglia errori lettura --> disconnetto e resetto
                if (numErroriCheck > maxErroriCheck)
                {
                    lgError($"numErroriCheck: {numErroriCheck} --> disconnessione adapter con tryDisconnect");

                    numErroriCheck = 0;
                    connectionOk = false;
                    tryDisconnect();
                }
            }
            return answ;
        }

        /// <summary>
        /// Effettua impostazione del valore numerico COMMESSA (specifico FANUC)
        /// </summary>
        /// <returns></returns>
        public bool setNumCom(int valReq)
        {
            bool answ = false;
            string topic = $"FanucMemRW.setNumCom";
            // verifico quale modalità sia richiesta
            string memAddr = memAddrCom;
            lgTrace($"INIT setNumCom | memAddr: {memAddr}");
            if (!string.IsNullOrEmpty(memAddr))
            {
                lgTrace($"setNumCom | memAddr: {memAddr} | valReq: {valReq}");
                // scrivo valore richiesto in area configurata
                try
                {
                    // var di appoggio
                    int cntAddr = 0;
                    // verifico se si tratta di lettura MACRO... formato tipo MACRO.6711
                    if (memAddr.StartsWith("MACRO."))
                    {
                        // recupero parametro...
                        int.TryParse(memAddr.Replace("MACRO.", ""), out cntAddr);
                        if (cntAddr == 0)
                        {
                            cntAddr = 9999;
                        }
                        lgTrace($"MACRO | Com Write 01 | memAddr: {memAddr} | idx: {cntAddr}");
                        // processo SET contapezzi (lavorati)
                        sw.Restart();
                        double vTransf = (double)valReq;
                        answ = FanucMemMacroRW(true, cntAddr, ref vTransf);
                        lgTrace($"MACRO | Com Write 02 | memAddr: {memAddr} | valReq: {valReq} | vTransf: {vTransf}");
                        if (utils.CRB("recTime"))
                        {
                            TimingData.addResult(IOBConfFull.General.CodIOB, "MACRO-SHORT", sw.ElapsedTicks);
                        }
                    }
                    sw.Stop();
                    tryReduceErrorCount(topic);
                }
                catch (Exception exc)
                {
                    tryIncreaseErrorComm(topic);
                    lgError($"Errore in setNumCom | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
                }
                // se supero soglia errori lettura --> disconnetto e resetto
                if (numErroriCheck > maxErroriCheck)
                {
                    lgError($"numErroriCheck: {numErroriCheck} --> disconnessione adapter con tryDisconnect");

                    numErroriCheck = 0;
                    connectionOk = false;
                    tryDisconnect();
                }
                answ = true;
            }
            return answ;
        }

        /// <summary>
        /// Effettua impostazione del conteggio pezzi richiesti
        /// </summary>
        /// <returns></returns>
        public override bool setPzComm(int pzReq)
        {
            string topic = $"FanucMemRW.setPzComm";
            bool answ = false;
            // ...SE abilitato da conf IOB
            if (IOBConfFull.Counters.EnableSetPzReq)
            {
                // scrivo valore 0 x il contapezzi
                try
                {
                    string memAddr = memAddrPzReq;
                    lgTrace($"INIT setPzComm | memAddr: {memAddr}");
                    // contapezzi ATTUALE
                    if (!string.IsNullOrEmpty(memAddr))
                    {
                        // verifico quale modalità sia richiesta: STD (6713) oppure BIT (Custom, con
                        // indicazione area)
                        if (memAddr.StartsWith("STD"))
                        {
                            // inizio verifica area memoria/parametro levando prima parte codice
                            memAddr = memAddr.Replace("STD.", "");
                        }
                        // var di appoggio
                        int cntAddr = 0;
                        // var contapezzi a zero....
                        object newVal = new object();
                        newVal = pzReq;
                        // verifico se si tratta di lettura parametro... formato tipo STD.PAR.6713
                        if (memAddr.StartsWith("PAR."))
                        {
                            // recupero parametro...
                            int.TryParse(memAddr.Replace("PAR.", ""), out cntAddr);
                            if (cntAddr == 0)
                            {
                                cntAddr = 6713;
                            }

                            // processo SET contapezzi (lavorati)
                            sw.Restart();
                            answ = FANUC_ref.F_RW_Param_Integer(true, cntAddr, 3, ref newVal);
                            if (utils.CRB("recTime"))
                            {
                                TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("W{0}-PAR", 4), sw.ElapsedTicks);
                            }
                        }
                        else if (memAddr.StartsWith("MACRO."))
                        {
                            // recupero parametro...
                            int.TryParse(memAddr.Replace("MACRO.", ""), out cntAddr);
                            if (cntAddr == 0)
                            {
                                cntAddr = 9999;
                            }
                            lgTrace($"MACRO | Pz Write 01 | memAddr: {memAddr} | idx: {cntAddr}");
                            // processo SET contapezzi (lavorati)
                            sw.Restart();
                            // se > 2^16 scrivo con INT, sennò con double...
                            short valTrShr = 0;
                            double valTrDbl = 0;
                            if (pzReq < Math.Pow(2, 16))
                            {
                                valTrShr = (short)pzReq;
                                answ = FanucMemMacroRW(true, cntAddr, ref valTrShr);
                                //answ = FANUC_ref.F_RW_Macro_Short(true, cntAddr, ref valTrShr);
                                lgTrace($"MACRO | Com Write 02 SHORT | memAddr: {memAddr} | pzCnt: {pzReq} | valTrShr: {valTrShr}");
                            }
                            // sennò double!
                            else
                            {
                                valTrDbl = (double)pzReq;
                                answ = FanucMemMacroRW(true, cntAddr, ref valTrDbl);
                                //answ = FANUC_ref.F_RW_Macro_Double(true, cntAddr, ref valTrDbl);
                                lgTrace($"MACRO | Com Write 02 DOUBLE | memAddr: {memAddr} | pzCnt: {pzReq} | valTrDbl: {valTrDbl}");
                            }
                            if (utils.CRB("recTime"))
                            {
                                TimingData.addResult(IOBConfFull.General.CodIOB, "MACRO-SHORT", sw.ElapsedTicks);
                            }
                        }
                        // altrimenti se legge da area memoria specifica leggo da li... formto tipo STD.D.1604.DW
                        else
                        {
                            memAddressFanuc areaCounter = new memAddressFanuc(memAddr);

                            if (isVerboseLog)
                            {
                                lgInfo("setPzComm [0] area memoria: {0}.{1}.{2}", areaCounter.mType, areaCounter.mPos, areaCounter.vType);
                            }
                            // leggo!
                            sw.Restart();
                            // switch x tipo dati --> tipo lettura... e salvo ultimo conteggio rilevato
                            switch (areaCounter.vType)
                            {
                                case "B":
                                    byte valB = (byte)pzReq;
                                    answ = FANUC_ref.F_RW_Byte(true, areaCounter.mType, areaCounter.mPos, ref valB);
                                    newVal = valB;
                                    break;

                                case "D":
                                    ushort valW = (ushort)pzReq;
                                    answ = FANUC_ref.F_RW_Word(true, areaCounter.mType, areaCounter.mPos, ref valW);
                                    newVal = valW;
                                    break;

                                case "DW":
                                    uint valDW = (uint)pzReq;
                                    answ = FANUC_ref.F_RW_DWord(true, areaCounter.mType, areaCounter.mPos, ref valDW);
                                    break;

                                default:
                                    break;
                            }
                            if (utils.CRB("recTime"))
                            {
                                TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("W-{0}.{1}.{2}", areaCounter.mType, areaCounter.mPos, areaCounter.vType), sw.ElapsedTicks);
                            }
                        }
                        sw.Stop();
                        tryReduceErrorCount(topic);
                    }
                }
                catch (Exception exc)
                {
                    tryIncreaseErrorComm(topic);
                    lgError($"Errore in SET PzReq Commessa | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
                }
                // se supero soglia errori lettura --> disconnetto e resetto
                if (numErroriCheck > maxErroriCheck)
                {
                    lgError($"numErroriCheck: {numErroriCheck} --> disconnessione adapter con tryDisconnect");

                    numErroriCheck = 0;
                    connectionOk = false;
                    tryDisconnect();
                }
                answ = true;
            }
            return answ;
        }

        /// <summary>
        /// Override connessione
        /// </summary>
        public override void tryConnect()
        {
            lgInfo("++++++++++++ START CONNECT ++++++++++++");
            if (!connectionOk)
            {
                // controllo che il ping sia stato tentato almeno pingTestSec fa...
                if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec"))
                {
                    if (verboseLog || periodicLog)
                    {
                        lgInfo("FANUC: ConnKO - tryConnect");
                    }
                    // in primis salvo data ping...
                    lastPING = DateTime.Now;
                    // ora PING!!!
                    Ping pingSender = new Ping();
                    IPAddress address = IPAddress.Loopback;
                    IPAddress.TryParse(IOBConfFull.Device.Connect.IpAddr, out address);
                    PingReply reply;
                    try
                    {
                        // se != null --> uso address...
                        if (address != null)
                        {
                            reply = pingSender.Send(address, 100);
                        }
                        else
                        {
                            reply = pingSender.Send(IOBConfFull.Device.Connect.IpAddr, 100);
                        }
                    }
                    catch
                    {
                        reply = pingSender.Send(IPAddress.Loopback, 100);
                    }
                    // se passa il ping faccio il resto...
                    if (reply.Status == IPStatus.Success)
                    {
                        string szStatusConnection = "";
                        try
                        {
                            // ora provo connessione...
                            parentForm.commPlcActive = true;
                            // se connesso disconnetto...
                            if (FANUC_ref.Connected)
                            {
                                FANUC_ref.Disconnect(ref szStatusConnection);
                                lgInfo($"FANUC_ref preliminary DISCONNECT | connStatus: {FANUC_ref.Connected}| szStatusConnection: {szStatusConnection}");
                                // aspetto
                                Thread.Sleep(500);
                            }
                            FANUC_ref.Connect(ref szStatusConnection);
                            parentForm.commPlcActive = false;
                            lgInfo($"FANUC_ref CONNECT | connected: {FANUC_ref.Connected}| szStatusConnection: {szStatusConnection}");
                            connectionOk = FANUC_ref.Connected;

                            // refresh stato allarmi!!!
                            if (connectionOk)
                            {
                                checkVetoQueueIn();
                                dtAvvioAdp = DateTime.Now;
                                if (adpRunning)
                                {
                                    lgInfo("Connessione OK");
                                }
                            }
                            else
                            {
                                lgError("Impossibile procedere, connessione mancante...");
                            }
                        }
                        catch (Exception exc)
                        {
                            lgFatal($"Errore nella connessione all'adapter FANUC: {szStatusConnection}{Environment.NewLine}{exc}");
                            connectionOk = false;
                            lgInfo($"Eccezione in TryConnect, Adapter NON running, pausa di {utils.CRI("waitRecMSec")} msec prima di ulteriori tentativi di riconnessione");
                        }
                    }
                    else
                    {
                        // loggo no risposta ping ...
                        connectionOk = false;
                        lgDebug($"Attenzione: controllo PING fallito per IP {IOBConfFull.Device.Connect.PingIpAddr} - {reply.Status}");
                    }
                }
            }
            // se non è ancora connesso faccio processng memoria caso disconnesso...
            if (!connectionOk)
            {
                // processo semafori ed invio...
                processMemoryDiscon();
            }
        }

        /// <summary>
        /// Override disconnessione
        /// </summary>
        public override void tryDisconnect()
        {
            lgDebug("----------- DISCONNECT -----------");
            if (connectionOk)
            {
                string szStatusConnection = "";
                try
                {
                    FANUC_ref.Disconnect(ref szStatusConnection);
                    Thread.Sleep(500);
                    connectionOk = false;
                    // resetto timing!
                    TimingData.resetData();
                    lgInfo($"Effettuata disconnessione adapter FANUC! | connectred: {FANUC_ref.Connected} | szStatusConnection: {szStatusConnection}");
                }
                catch (Exception exc)
                {
                    lgFatal(exc, "Errore nella disconnessione dall'adapter FANUC");
                }
            }
            else
            {
                lgDebug("Disconnessione iniziale non necessaria");
            }
            queueInEnabCurr = false;
        }

        #endregion Public Methods

        #region Internal Properties

        /// <summary>
        /// LookUpTable di decodifica da CNC a segnali tipo bitmap MAPO
        /// </summary>
        internal Dictionary<string, string> signLUT
        {
            get => IOBConfFull.Device.SigLUT;
        }

        #endregion Internal Properties

        #region Protected Fields

        /// <summary>
        /// Dati dell'area D
        /// </summary>
        protected memAreaFanuc areaD;

        /// <summary>
        /// Dati dell'area PARameters
        /// </summary>
        protected memAreaFanuc areaPAR;

        /// <summary>
        /// Dati dell'area R
        /// </summary>
        protected memAreaFanuc areaR;

        /// <summary>
        /// Dati dell'area X
        /// </summary>
        protected memAreaFanuc areaX;

        /// <summary>
        /// Dati dell'area Y
        /// </summary>
        protected memAreaFanuc areaY;

        /// <summary>
        /// Oggetto MAIN x connessione FANUC
        /// </summary>
        protected FANUC FANUC_ref;

        /// <summary>
        /// Area memoria G (copia)
        /// </summary>
        protected byte[] MemBlockG = new byte[2];

        /// <summary>
        /// Area memoria R (copia)
        /// </summary>
        protected byte[] MemBlockR = new byte[2];

        /// <summary>
        /// Area memoria X (copia)
        /// </summary>
        protected byte[] MemBlockX = new byte[2];

        /// <summary>
        /// Area memoria Y (copia)
        /// </summary>
        protected byte[] MemBlockY = new byte[2];

        #endregion Protected Fields

        #region Protected Methods

        /// <summary>
        /// decodifica il modo dai valori del byte G43
        /// </summary>
        /// <param name="currVal"></param>
        /// <returns></returns>
        protected static CNC_MODE decodeG43(byte currVal)
        {
            // hard coded da valori tabellari a MODI definiti in CNC_MODE...
            CNC_MODE answ = CNC_MODE.ND;
            switch (currVal)
            {
                case 0:
                    answ = CNC_MODE.MDI;
                    break;

                case 1:
                    answ = CNC_MODE.MEN;
                    break;

                case 3:
                    answ = CNC_MODE.EDIT;
                    break;

                case 4:
                    answ = CNC_MODE.HANDLE_INC;
                    break;

                case 5:
                    answ = CNC_MODE.JOG;
                    break;

                case 6:
                    answ = CNC_MODE.TJOG;
                    break;

                case 7:
                    answ = CNC_MODE.THND;
                    break;

                case 33:
                    answ = CNC_MODE.RMT;
                    break;

                case 133:
                    answ = CNC_MODE.REF;
                    break;

                default:
                    answ = CNC_MODE.ND;
                    break;
            }
            return answ;
        }

        /// <summary>
        /// Metodo da overridare x scrivere DAVVERO i parametri sul PLC
        /// </summary>
        /// <param name="updatedPar"></param>
        protected override void plcWriteParams(ref List<objItem> updatedPar)
        {
            lgTrace($"plcWriteParams: richiesta per {updatedPar.Count} params");
            foreach (var item in updatedPar)
            {
                lgInfo($"ITEM | {item.uid} | {item.value}");
                // salvo i valori ricevuti
                upsertKey(item.uid, item.reqValue);
                // se i valori richiesto e fatto corrispondono... resetto!
                if (item.reqValue.Trim() == item.value.Trim())
                {
                    // resetto richiesta!
                    item.reqValue = "";
                }
            }
        }

        protected bool setPzCnt(int pzCnt)
        {
            string topic = $"FanucMemRW.setPzCnt";
            bool answ = false;
            // ...SE abilitato da conf IOB
            if (IOBConfFull.Counters.EnableSetPzCount)
            {
                // scrivo valore del contapezzi
                try
                {
                    string memAddr = memAddrPzCnt;
                    lgTrace($"INIT setPzCnt | memAddr: {memAddr}");
                    // contapezzi ATTUALE
                    if (!string.IsNullOrEmpty(memAddr))
                    {
                        // verifico quale modalità sia richiesta: STD (6711) oppure BIT (Custom, con
                        // indicazione area)
                        if (memAddr.StartsWith("STD"))
                        {
                            // inizio verifica area memoria/parametro levando prima parte codice
                            memAddr = memAddr.Replace("STD.", "");
                        }
                        // var di appoggio
                        int cntAddr = 0;
                        // var contapezzi a zero....
                        object newVal = new object();
                        newVal = pzCnt;
                        // verifico se si tratta di lettura parametro... formato tipo STD.PAR.6711
                        if (memAddr.StartsWith("PAR."))
                        {
                            // recupero parametro...
                            int.TryParse(memAddr.Replace("PAR.", ""), out cntAddr);
                            if (cntAddr == 0)
                            {
                                cntAddr = 6711;
                            }

                            // processo SET contapezzi (lavorati)
                            sw.Restart();
                            FANUC_ref.F_RW_Param_Integer(true, cntAddr, 3, ref newVal);
                            if (utils.CRB("recTime"))
                            {
                                TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("W{0}-PAR", 4), sw.ElapsedTicks);
                            }
                        }
                        else if (memAddr.StartsWith("MACRO."))
                        {
                            // recupero parametro...
                            int.TryParse(memAddr.Replace("MACRO.", ""), out cntAddr);
                            if (cntAddr == 0)
                            {
                                cntAddr = 9999;
                            }
                            lgTrace($"MACRO | Pz Write 01 | memAddr: {memAddr} | idx: {cntAddr}");
                            // processo SET contapezzi (lavorati)
                            sw.Restart();
                            // se > 2^16 scrivo con INT, sennò con double...
                            short valTrShr = 0;
                            double valTrDbl = 0;
                            if (pzCnt < Math.Pow(2, 16))
                            {
                                valTrShr = (short)pzCnt;
                                answ = FanucMemMacroRW(true, cntAddr, ref valTrShr);
                                //answ = FANUC_ref.F_RW_Macro_Short(true, cntAddr, ref valTrShr);
                                lgTrace($"MACRO | Com Write 02 SHORT | memAddr: {memAddr} | pzCnt: {pzCnt} | valTrShr: {valTrShr}");
                            }
                            // sennò double!
                            else
                            {
                                valTrDbl = (double)pzCnt;
                                answ = FanucMemMacroRW(true, cntAddr, ref valTrDbl);
                                //answ = FANUC_ref.F_RW_Macro_Double(true, cntAddr, ref valTrDbl);
                                lgTrace($"MACRO | Com Write 02 DOUBLE | memAddr: {memAddr} | pzCnt: {pzCnt} | valTrDbl: {valTrDbl}");
                            }
                            if (utils.CRB("recTime"))
                            {
                                TimingData.addResult(IOBConfFull.General.CodIOB, "MACRO-SHORT", sw.ElapsedTicks);
                            }
                        }
                        // altrimenti se legge da area memoria specifica leggo da li... formto tipo STD.D.1604.DW
                        else
                        {
                            memAddressFanuc areaCounter = new memAddressFanuc(memAddr);

                            if (isVerboseLog)
                            {
                                lgInfo("setPzComm [0] area memoria: {0}.{1}.{2}", areaCounter.mType, areaCounter.mPos, areaCounter.vType);
                            }
                            // leggo!
                            sw.Restart();
                            // switch x tipo dati --> tipo lettura... e salvo ultimo conteggio rilevato
                            switch (areaCounter.vType)
                            {
                                case "B":
                                    byte valB = (byte)pzCnt;
                                    FANUC_ref.F_RW_Byte(true, areaCounter.mType, areaCounter.mPos, ref valB);
                                    newVal = valB;
                                    break;

                                case "D":
                                    ushort valW = (ushort)pzCnt;
                                    FANUC_ref.F_RW_Word(true, areaCounter.mType, areaCounter.mPos, ref valW);
                                    newVal = valW;
                                    break;

                                case "DW":
                                    uint valDW = (uint)pzCnt;
                                    FANUC_ref.F_RW_DWord(true, areaCounter.mType, areaCounter.mPos, ref valDW);
                                    break;

                                default:
                                    break;
                            }
                            if (utils.CRB("recTime"))
                            {
                                TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("W-{0}.{1}.{2}", areaCounter.mType, areaCounter.mPos, areaCounter.vType), sw.ElapsedTicks);
                            }
                        }
                        sw.Stop();
                        tryReduceErrorCount(topic);
                    }
                }
                catch (Exception exc)
                {
                    tryIncreaseErrorComm(topic);
                    lgError($"Errore in SET PzCnt | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
                }
                // se supero soglia errori lettura --> disconnetto e resetto
                if (numErroriCheck > maxErroriCheck)
                {
                    lgError($"numErroriCheck: {numErroriCheck} --> disconnessione adapter con tryDisconnect");

                    numErroriCheck = 0;
                    connectionOk = false;
                    tryDisconnect();
                }
                answ = true;
            }
            return answ;
        }

        #endregion Protected Methods

        #region Private Fields

        /// <summary>
        /// Oggetto logger della classe
        /// </summary>
        private static Logger _logger = LogManager.GetCurrentClassLogger();

        #endregion Private Fields

        #region Private Properties

        /// <summary>
        /// Area memoria Articolo (numerico o stringa)
        /// </summary>
        private string memAddrArt
        {
            get
            {
                string memAddr = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("setArtNum"))
                    {
                        memAddr = memMap.mMapWrite["setArtNum"].memAddr;
                    }
                    // se vuoto provo altra alternativa...
                    if (string.IsNullOrEmpty(memAddr) && memMap.mMapWrite.ContainsKey("setArt"))
                    {
                        memAddr = memMap.mMapWrite["setArt"].memAddr;
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memAddr))
                {
                    memAddr = IOBConfFull.Device.NumArtMode;
                }
                // bonifica variabile da definizione STD iniziale...
                if (memAddr.StartsWith("STD"))
                {
                    memAddr = memAddr.Replace("STD.", "");
                }
                return memAddr;
            }
        }

        /// <summary>
        /// Area memoria cadenza
        /// </summary>
        private string memAddrCaden
        {
            get
            {
                string memAddr = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("Cadenza"))
                    {
                        memAddr = memMap.mMapWrite["Cadenza"].memAddr;
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memAddr))
                {
                    memAddr = IOBConfFull.Device.PzCadMode;
                }
                // bonifica variabile da definizione STD iniziale...
                if (memAddr.StartsWith("STD"))
                {
                    memAddr = memAddr.Replace("STD.", "");
                }
                return memAddr;
            }
        }

        /// <summary>
        /// Area memoria commessa
        /// </summary>
        private string memAddrCom
        {
            get
            {
                string memAddr = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("setCommNum"))
                    {
                        memAddr = memMap.mMapWrite["setCommNum"].memAddr;
                    }
                    // se vuoto provo altra alternativa...
                    if (string.IsNullOrEmpty(memAddr) && memMap.mMapWrite.ContainsKey("setComm"))
                    {
                        memAddr = memMap.mMapWrite["setComm"].memAddr;
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memAddr))
                {
                    memAddr = IOBConfFull.Device.NumOdlMode;
                }
                // bonifica variabile da definizione STD iniziale...
                if (memAddr.StartsWith("STD"))
                {
                    memAddr = memAddr.Replace("STD.", "");
                }
                return memAddr;
            }
        }

        /// <summary>
        /// Area memoria contapezzi (parziale/corrente)
        /// </summary>
        private string memAddrPzCnt
        {
            get
            {
                string memAddr = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("ContatoreParziale"))
                    {
                        memAddr = memMap.mMapWrite["ContatoreParziale"].memAddr;
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memAddr))
                {
                    memAddr = IOBConfFull.Device.PzCountMode;
                }
                // bonifica variabile da definizione STD iniziale...
                if (memAddr.StartsWith("STD"))
                {
                    memAddr = memAddr.Replace("STD.", "");
                }
                return memAddr;
            }
        }

        /// <summary>
        /// Area memoria PzCount FreeCounter
        /// </summary>
        private string memAddrPzCntFree
        {
            get
            {
                string memAddr = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("PzCntFree"))
                    {
                        memAddr = memMap.mMapWrite["PzCntFree"].memAddr;
                    }
                }
                // bonifica variabile da definizione STD iniziale...
                if (memAddr.StartsWith("STD"))
                {
                    memAddr = memAddr.Replace("STD.", "");
                }
                return memAddr;
            }
        }

        /// <summary>
        /// Area memoria contapezzi assoluto /GTot
        /// </summary>
        private string memAddrPzCntTot
        {
            get
            {
                string memAddr = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("ContatoreAssoluto"))
                    {
                        memAddr = memMap.mMapWrite["ContatoreAssoluto"].memAddr;
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memAddr))
                {
                    memAddr = IOBConfFull.Device.PzTotMode;
                }
                // bonifica variabile da definizione STD iniziale...
                if (memAddr.StartsWith("STD"))
                {
                    memAddr = memAddr.Replace("STD.", "");
                }
                return memAddr;
            }
        }

        /// <summary>
        /// Area memoria contatore pezzi richiesti
        /// </summary>
        private string memAddrPzReq
        {
            get
            {
                string memAddr = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("setPzComm"))
                    {
                        memAddr = memMap.mMapWrite["setPzComm"].memAddr;
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memAddr))
                {
                    memAddr = IOBConfFull.Device.PzReqMode;
                }
                // bonifica variabile da definizione STD iniziale...
                if (memAddr.StartsWith("STD"))
                {
                    memAddr = memAddr.Replace("STD.", "");
                }
                return memAddr;
            }
        }

        /// <summary>
        /// Area memoria Articolo
        /// </summary>
        private string memNameArt
        {
            get
            {
                string memName = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("setArtNum"))
                    {
                        memName = "setArtNum";
                    }
                    // se vuoto provo altra alternativa...
                    if (string.IsNullOrEmpty(memName) && memMap.mMapWrite.ContainsKey("setArt"))
                    {
                        memName = "setArt";
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memName))
                {
                    memName = "SET_NUM_ART";
                }
                return memName;
            }
        }

        private string memNameCaden
        {
            get
            {
                string memName = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("Cadenza"))
                    {
                        memName = "Cadenza";
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memName))
                {
                    memName = "CICLE_CAD";
                }
                return memName;
            }
        }

        private string memNameCom
        {
            get
            {
                string memName = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("setCommNum"))
                    {
                        memName = "setCommNum";
                    }
                    // se vuoto provo altra alternativa...
                    if (string.IsNullOrEmpty(memName) && memMap.mMapWrite.ContainsKey("setComm"))
                    {
                        memName = "setComm";
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memName))
                {
                    memName = "SET_NUM_COM";
                }
                return memName;
            }
        }

        private string memNamePzCnt
        {
            get
            {
                string memName = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("PzCntFree"))
                    {
                        memName = "PzCntFree";
                    }
                    else if (memMap.mMapWrite.ContainsKey("ContatoreParziale"))
                    {
                        memName = "ContatoreParziale";
                    }
                    else if (memMap.mMapRead.ContainsKey("PZ_COUNT"))
                    {
                        memName = "PZ_COUNT";
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memName))
                {
                    memName = "PZCOUNT_MODE";
                }
                return memName;
            }
        }

        private string memNamePzCntFree
        {
            get
            {
                string memName = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("ContatoreParziale"))
                    {
                        memName = "ContatoreParziale";
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memName))
                {
                    memName = "PZ_COUNT";
                }
                return memName;
            }
        }

        private string memNamePzCntTot
        {
            get
            {
                string memName = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("ContatoreAssoluto"))
                    {
                        memName = "ContatoreAssoluto";
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memName))
                {
                    memName = "PZ_GTOT";
                }
                return memName;
            }
        }

        private string memNamePzReq
        {
            get
            {
                string memName = "";
                if (memMap != null && memMap.mMapWrite != null)
                {
                    //cerco in primis modalità NUM altrimenti standard...
                    if (memMap.mMapWrite.ContainsKey("setPzComm"))
                    {
                        memName = "setPzComm";
                    }
                }
                // se non trovato... leggo da OPT_PAR
                if (string.IsNullOrEmpty(memName))
                {
                    memName = "PZ_REQ";
                }
                return memName;
            }
        }

        #endregion Private Properties

        #region Private Methods

        /// <summary>
        /// Calcola il valore bitmap segnale in uscita data la bVal (mem area + bit) + indice i
        /// </summary>
        /// <param name="bVal">Conf memoria da signalLUT</param>
        /// <param name="i">Indice del bit x calcolo out</param>
        /// <param name="byte2check">Byte completo recuperato</param>
        /// <param name="bitNum">Indice bit cercato</param>
        /// <returns></returns>
        private int CalcAreaBitOut(string bVal, int i, ref int byte2check, ref int bitNum)
        {
            // OUT valore
            int outVal = 0;
            // var accessorie
            int byteNum = 0;
            bool invSignal = false;
            char area;
            string memArea = "";
            string[] memIdx;
            // di norma è segnale normale => 1, altrimenti inverse => 0...
            // cerco se sia inverse (primo char "!") --> registro e elimino char...
            invSignal = bVal.StartsWith("!");
            // tolgo comunque inversione...
            bVal = bVal.Replace("!", "");
            // recupero area...
            area = bVal[0];
            // altrimenti decodifico area...
            memArea = bVal.Substring(1, bVal.Length - 1);
            memIdx = memArea.Split('.');
            // calcolo bit e byte number...
            int.TryParse(memIdx[0], out byteNum);
            if (memIdx.Length > 1)
            {
                int.TryParse(memIdx[1], out bitNum);
            }
            // in base al nome cerco in una delle aree.. e prendo solo solo quel bit
            // di quel byte...
            switch (area)
            {
                case 'G':
                    byte2check = MemBlockG[byteNum];
                    break;

                case 'R':
                    byte2check = MemBlockR[byteNum];
                    break;

                case 'X':
                    byte2check = MemBlockX[byteNum];
                    break;

                case 'Y':
                    byte2check = MemBlockY[byteNum];
                    break;

                default:
                    break;
            }
            // a secondo che sia segnale normale o inverso...
            if (invSignal)
            {
                // controllo se il bit sia NON attivo (basso)... == 0...
                if ((byte2check & (1 << bitNum)) == 0)
                {
                    outVal += 1 << i;
                }
            }
            else
            {
                // controllo se il bit sia attivo (alto)... != 0
                if ((byte2check & (1 << bitNum)) != 0)
                {
                    outVal += 1 << i;
                }
            }
            return outVal;
        }

        /// <summary>
        /// Calcola valore bitmap segnale in uscita data condizione programName
        /// </summary>
        /// <param name="confTest">Conf search programma: nome e se inizia per % ricerca LIKE</param>
        /// <param name="i">Indice del bit x calcolo out</param>
        private int CalProgNameBitOut(string confTest, int i)
        {
            // OUT valore
            int outVal = 0;
            string prgNameTgt = "";
            // il valore da cercare è quello inserito dopo il segno pipe...
            if (confTest.Contains("|"))
            {
                var prgData = confTest.Split('|');
                if (prgData.Length > 1)
                {
                    prgNameTgt = prgData[1];
                }
            }
            // ora verifico se prog corrente è valido
            if (!string.IsNullOrEmpty(currPrgName))
            {
                // se contiene % allora cerco x like
                if (prgNameTgt.Contains("%"))
                {
                    // e se sia uguale al valore tgt senza %
                    if (currPrgName.Contains(prgNameTgt.Replace("%", "")))
                    {
                        // inserisco bit!
                        outVal += 1 << i;
                    }
                }
                // altrimenti ricerca puntuale (prima versione)
                else
                {
                    // e se sia uguale al valore tgt
                    if (currPrgName == prgNameTgt)
                    {
                        // inserisco bit!
                        outVal += 1 << i;
                    }
                }
            }
            return outVal;
        }

        /// <summary>
        /// Verifica se effettuare invio (a scadenza) del valore counter richiesto
        /// </summary>
        /// <param name="varName"></param>
        /// <param name="currMemName"></param>
        /// <param name="currMemAddr"></param>
        private async Task checkSendCounter(string varName, string currMemName, string currMemAddr)
        {
            string topic = $"FanucMemRW.checkSendCounter.{currMemName}";
            // gestione con ricerca in memoria Write / optPar...
            if (string.IsNullOrEmpty(currMemAddr))
            {
                lgTrace($"{varName} disabilitato | currMemAddr vuoto | NO memConf.Read | NO optPar");
            }
            else
            {
                try
                {
                    DateTime adesso = DateTime.Now;
                    int currPeriod = memPeriod(currMemName);
                    // se non ha periodo abilito invio
                    bool doSend = currPeriod == 0;
                    string vetoType = "DEFAULT";
                    if (currPeriod == 0)
                    {
                        // ma imposto cmq un poeriodo veto a 15 min...
                        currPeriod = 15 * 60;
                    }
                    else
                    {
                        vetoType = "EXPLICIT";
                    }

                    // controllo comunque scadenza...
                    if (VetoCounterSend.ContainsKey(currMemName))
                    {
                        // verifico scadenza...
                        if (VetoCounterSend[currMemName] < adesso)
                        {
                            VetoCounterSend[currMemName] = adesso.AddSeconds(currPeriod);
                            doSend = true;
                            lgInfo($"checkSendCounter | set veto {vetoType} ({currPeriod}) for {varName} at {VetoCounterSend[currMemName]:HH:mm:ss}");
                        }
                    }
                    else
                    {
                        VetoCounterSend.Add(currMemName, adesso.AddSeconds(currPeriod));
                        doSend = true;
                        lgTrace($"{varName} | Veto {vetoType} still active until {VetoCounterSend[currMemName]:HH:mm:ss}");
                    }

                    // per il counter verifico SE sia da inviare...
                    if (doSend)
                    {
                        await sendOptVal(currMemName, getValByMemAddr(currMemAddr));
                        tryReduceErrorCount(topic);
                    }
                }
                catch (Exception exc)
                {
                    tryIncreaseErrorComm(topic);
                    lgError($"Eccezione | processOtherCounters.checkSendCounter | {varName} | {currMemName} | {currMemAddr} | numErroriCheck: {numErroriCheck}{Environment.NewLine}{exc}");
                    // attesa 10ms post errore...
                    await Task.Delay(10);
                }
                // se supero soglia errori lettura --> disconnetto e resetto
                if (numErroriCheck > maxErroriCheck)
                {
                    lgError($"numErroriCheck: {numErroriCheck} --> disconnessione adapter con tryDisconnect");

                    numErroriCheck = 0;
                    connectionOk = false;
                    tryDisconnect();
                }
            }
        }

        /// <summary>
        /// Effettua decodifica aree memoria alla bitmap usata x MAPO
        /// </summary>
        private void decodeToBitmap()
        {
            // init a zero...
            B_input = 0;
            if (connectionOk)
            {
                // SE SI E' CONNESSO al FANUC allora è 1=powerON...
                if (FANUC_ref.Connected)
                {
                    B_input += 1 << 0;
                }

                // decodifico impiegando dictionary... cercando il TIPO di memoria & co...
                string bKey = "";
                string bVal = "";
                // valore INVERTED (default è false)
                bool invSignal = false;
                int bitNum = 0;
                int byte2check = 0;
                for (int i = 0; i < 8; i++)
                {
                    bKey = $"BIT{i}";
                    // cerco se ci sia in LUT
                    if (signLUT.ContainsKey(bKey))
                    {
                        // recupero nome variabile...
                        bVal = signLUT[bKey];
                        // se l'area è PZCOUNT... processo PUNTUALMENTE il CONTAPEZZI...
                        if (bVal.StartsWith("PZCOUNT"))
                        {
                            // procedo SOLO SE è enabled IOB
                            if (IobOnline)
                            {
                                try
                                {
                                    // 2026.04.24 controllo ODL solo se son passati ALMENO 10 sec
                                    int newIdx = CurrOdl();
                                    currODL = $"{currIdxODL}";
#if DEBUG
                                    lgInfo($"FANUC | Verifica ODL | IdxODL {currIdxODL}");
#else
                lgDebug($"FANUC | Verifica ODL | IdxODL {currIdxODL}");
#endif
                                }
                                catch (Exception exc)
                                {
                                    if (DateTime.Now.Subtract(lastWarnODL).TotalSeconds > 15)
                                    {
                                        lgError(exc, "Errore in fase di chiamata URL x ODL corrente | URL chiamato: {0}", urlGetCurrODL);
                                        lastWarnODL = DateTime.Now;
                                    }
                                }
                            }
                            else
                            {
                                // imposto currODL a vuoto!
                                currODL = "";
                                if (periodicLog)
                                {
                                    lgInfo($"Fanuc | Lettura ODL non effettuata: IobOnline: {IobOnline} | currODL impostato a vuoto");
                                }
                            }
                            // verifico NON sia vietato gestione contapezzi
                            if (!plcPzCountValid)
                            //if (!plcPzCountValid || !rawInputRead)
                            {
                                lgInfo($"Veto check contapezzi e ODL fino alle {dtVetoReadPzCount}");
                            }
                            else
                            {
                                //if (!string.IsNullOrEmpty(currODL) && currODL != "0")
                                if (!string.IsNullOrEmpty(currODL) && currIdxODL > 0)
                                {
                                    // controllo se è passato intervallo minimo tra 2
                                    // controlli/elaborazioni x distanziare invio e ridurre letture
                                    if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay))
                                    {
                                        // se sono differenti MOSTRO...
                                        if (contapezziPLC != contapezziIOB)
                                        {
                                            // registro contapezzi
                                            lgInfo($"Differenza Contapezzi: contapezziPLC: {contapezziPLC} | contapezziIOB {contapezziIOB}");
                                        }

                                        if (enablePzCountByApp)
                                        {
                                            // verifico se variato contapezzi...
                                            if (contapezziPLC > contapezziIOB)
                                            {
                                                // salvo nuovo contapezzi (incremento di 1...) +
                                                // richiesta refresh conteggio
                                                contapezziIOB++;
                                                needRefreshPzCount = true;
                                                // salvo in semaforo!
                                                B_input += 1 << 2;
                                                // registro contapezzi
                                                lgInfo($"contapezziPLC FANUC: {contapezziPLC} | contapezziIOB {contapezziIOB}");
                                            }
                                            // invio a server contapezzi (aggiornato)
                                            string retVal = utils.callUrl(urlSetPzCount + contapezziIOB.ToString());
                                            // verifica se tutto OK
                                            if (retVal != contapezziIOB.ToString())
                                            {
                                                // errore salvataggio contapezzi
                                                lgInfo($"Errore salvataggio Contapezzi FANUC: contapezziPLC {contapezziPLC} | contapezziIOB {contapezziIOB} | risposta: {retVal}");
                                                // rileggo il counter pezzi da server
                                                pzCntReload(true);
                                            }
                                            // resetto timer...
                                            lastPzCountSend = DateTime.Now;
                                        }
                                    }
                                }
                                else
                                {
                                    if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay))
                                    {
                                        lgInfo($"Attenzione: mancanza ODL non procedo con gestione contapezzi. contapezziPLC FANUC: {contapezziPLC} | contapezziIOB {contapezziIOB}");
                                        // resetto timer...
                                        lastPzCountSend = DateTime.Now;
                                    }
                                }
                            }
                        }
                        // altrimenti se è gestione nome prog corrente
                        else if (bVal.StartsWith("currPrgName"))
                        {
                            bool isMultiCond = bVal.Contains("/");
                            string val2Test = "";
                            int outVal = 0;
                            // in primis se ci fosse multicondizione separo...
                            if (isMultiCond)
                            {
                                var rawMultiCond = bVal.Split('/');
                                // il primo è test x programma...
                                val2Test = rawMultiCond[0];
                                // il secondo è test bit...
                                string sigTest = rawMultiCond[1];
                                // faccio test!
                                outVal = CalcAreaBitOut(sigTest, 0, ref byte2check, ref bitNum);
                            }
                            else
                            {
                                // test singola condizione prog name
                                val2Test = bVal;
                            }

                            // effettuo test del programma
                            int outProg = CalProgNameBitOut(val2Test, i);

                            // se è condizione singola o se la multi-condizion è garantita aggiungo...
                            if (!isMultiCond || outVal > 0)
                            {
                                // aggiungo!
                                B_input += outProg;
                            }
                        }
                        // area "normale" byte.bit
                        else
                        {
                            invSignal = false;
                            // cerco se sia inverse (primo char "!") --> registro e elimino char...
                            invSignal = bVal.StartsWith("!");
                            // se è da tracciare...
                            if (mem2trace.Contains($"|{bKey}|"))
                            {
                                if (invSignal)
                                {
                                    lgTrace($"Segnale invertito | bVal: {bVal}");
                                }
                            }
                            int outCalc = CalcAreaBitOut(bVal, i, ref byte2check, ref bitNum);
                            // aggiungo!
                            B_input += outCalc;

                            // se è da tracciare...
                            if (mem2trace.Contains($"|{bKey}|"))
                            {
                                var bMapAct = Convert.ToString(byte2check, 2).PadLeft(8, '0');
                                var bMapReq = Convert.ToString((1 << bitNum), 2).PadLeft(8, '0');
                                lgTrace($"Valore | bKey: {bKey} | bVal: {bVal} | byte: {bMapAct} | tgt: {bMapReq} | B_input: {B_input}");
                            }
                        }
                    }
                }
            }
            // log opzionale!
            if (verboseLog)
            {
                lgInfo(string.Format("Trasformazione B_input: {0}", B_input));
            }
        }

        /// <summary>
        /// Dump area D della memoria
        /// </summary>
        /// <param name="tipo">tipo di DUMP: START (sovrascrivendo) / SAMPLE (salva tanti campionamenti)</param>
        /// <param name="tipoMem">tipo memoria</param>
        /// <param name="memIndex">area memoria di partenza</param>
        /// <param name="memSizeByte">dimensione memoria</param>
        private void dump_MemArea(dumpType tipo, FANUC.MemType tipoMem, int memIndex, int memSizeByte)
        {
            DateTime adesso = DateTime.Now;
            string nomeFileB = "";
            string nomeFileW = "";
            string nomeFileDW = "";
            Dictionary<string, string> mappaValori = new Dictionary<string, string>();
            // per sicurezza verifico < 9999 byte
            if (memSizeByte > 9999)
            {
                memSizeByte = 9999;
            }
            // leggo TUTTI i (MAX 9999) byte della memoria D...
            byte[] MemBlockCurr = new byte[memSizeByte];
            if (verboseLog)
            {
                lgInfo("START MemDump", tipoMem);
            }

            sw.Restart();
            FanucMemRW(R, tipoMem, memIndex, ref MemBlockCurr);
            if (utils.CRB("recTime"))
            {
                TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("R{0}-{1}", MemBlockCurr.Length, tipoMem), sw.ElapsedTicks);
            }

            if (verboseLog)
            {
                lgInfo("END MemDump", tipoMem);
            }

            // seconda del tipo di lettura definisco i nomi delle variabili...
            if (tipo == dumpType.SAMPLE)
            {
                nomeFileB = string.Format(@"{0}\SAMPLES\{1}_{2}_Byte_{3:yyyyMMdd_HHmmss}.dat", utils.dataDatDir, IOBConfFull.General.CodIOB, tipoMem, adesso);
                nomeFileW = string.Format(@"{0}\SAMPLES\{1}_{2}_W_{3:yyyyMMdd_HHmmss}.dat", utils.dataDatDir, IOBConfFull.General.CodIOB, tipoMem, adesso);
                nomeFileDW = string.Format(@"{0}\SAMPLES\{1}_{2}_DW_{3:yyyyMMdd_HHmmss}.dat", utils.dataDatDir, IOBConfFull.General.CodIOB, tipoMem, adesso);
            }
            else
            {
                // salvo in file i dati letti come BYTE
                nomeFileB = string.Format(@"{0}\{1}_{2}_Byte.dat", utils.dataDatDir, IOBConfFull.General.CodIOB, tipoMem);
                nomeFileW = string.Format(@"{0}\{1}_{2}_W.dat", utils.dataDatDir, IOBConfFull.General.CodIOB, tipoMem);
                nomeFileDW = string.Format(@"{0}\{1}_{2}_DW.dat", utils.dataDatDir, IOBConfFull.General.CodIOB, tipoMem);
            }

            // salvo in file i dati letti come BYTE
            mappaValori = new Dictionary<string, string>();
            for (int i = 0; i < MemBlockCurr.Length; i++)
            {
                // versione pre dotNet8 (che usa "b" come stringa formato std)
                var bitMap = Convert.ToString(MemBlockCurr[i], 2).PadLeft(8, '0');
                mappaValori.Add($"[{i:0000}]", $"{bitMap}={MemBlockCurr[i]}");
                //mappaValori.Add($"[{i:0000}]", $"{MemBlockCurr[i]:b}={MemBlockCurr[i]}");
            }
            utils.WritePlain(mappaValori, nomeFileB);

            // salvo in file i dati letti come Word (2byte)
            mappaValori = new Dictionary<string, string>();
            for (int i = 0; i < MemBlockCurr.Length / 2; i++)
            {
                mappaValori.Add($"[{i:0000}]", BitConverter.ToUInt16(MemBlockCurr, i * 2).ToString());
            }
            utils.WritePlain(mappaValori, nomeFileW);

            // salvo in file i dati letti come DWord (4byte)
            mappaValori = new Dictionary<string, string>();
            for (int i = 0; i < MemBlockCurr.Length / 4; i++)
            {
                mappaValori.Add($"[{i:0000}]", BitConverter.ToUInt32(MemBlockCurr, i * 4).ToString());
            }
            utils.WritePlain(mappaValori, nomeFileDW);
        }

        /// <summary>
        /// Dump area PARAMETRI
        /// </summary>
        /// <param name="tipo">tipo di DUMP: START (sovrascrivendo) / SAMPLE (salva tanti campionamenti)</param>
        /// <param name="memIndex">Parametro di partenza</param>
        /// <param name="numPar">Numero parametri da esportare... memoria</param>
        private void dump_ParArea(dumpType tipo, int memIndex, int numPar)
        {
            DateTime adesso = DateTime.Now;
            string nomeFile = "";
            Dictionary<string, string> mappaValori = new Dictionary<string, string>();
            // per sicurezza verifico < 9999 parametri
            if (numPar > 9999)
            {
                numPar = 9999;
            }

            // leggo TUTTI i (MAX 9999) byte della memoria D...
            object[] paramsArray = new object[numPar];
            if (verboseLog)
            {
                lgInfo("START ParamDump");
            }

            sw.Restart();
            for (int i = 0; i < numPar; i++)
            {
                FANUC_ref.F_RW_Param_Integer(false, memIndex + i, 3, ref paramsArray[i]);
            }

            if (utils.CRB("recTime"))
            {
                TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("R{0}-PAR", 4 * numPar), sw.ElapsedTicks);
            }

            if (verboseLog)
            {
                lgInfo("END ParamDump");
            }

            // seconda del tipo di lettura definisco i nomi delle variabili...
            if (tipo == dumpType.SAMPLE)
            {
                nomeFile = string.Format(@"{0}\SAMPLES\{1}_{2}_{3:yyyyMMdd_HHmmss}.dat", utils.dataDatDir, IOBConfFull.General.CodIOB, "PAR", adesso);
            }
            else
            {
                nomeFile = string.Format(@"{0}\{1}_{2}.dat", utils.dataDatDir, IOBConfFull.General.CodIOB, "PAR");
            }

            // salvo in file i dati letti
            mappaValori = new Dictionary<string, string>();
            for (int i = 0; i < paramsArray.Length; i++)
            {
                mappaValori.Add(i.ToString("0000"), paramsArray[i].ToString());
            }
            utils.WritePlain(mappaValori, nomeFile);
        }

        /// <summary>
        /// Formatta area x memorie Fanuc da conf caricata
        /// </summary>
        /// <param name="aName"></param>
        /// <param name="mKey"></param>
        /// <returns></returns>
        private memAreaFanuc FormatAreaFanuc(string aName, string mKey)
        {
            var newArea = new memAreaFanuc() { areaName = aName };
            if (IOBConfFull.Special.BankConf.AreaConf.ContainsKey(mKey))
            {
                var thisConf = IOBConfFull.Special.BankConf.AreaConf[mKey];
                newArea.startIdx = thisConf.Start;
                newArea.arraySize = thisConf.Size;
            }

            return newArea;
        }

        /// <summary>
        /// Recupera il valore INT dal nome del parametro per successivo processing
        /// </summary>
        /// <param name="varName"></param>
        /// <returns></returns>
        private string getValByMemAddr(string memAddr)
        {
            lgTrace($"inizio getValByMemAddr | memAddr: {memAddr}");
            string answ = "";
            // verifico quale modalità sia richiesta: STD (6711) oppure BIT (Custom, con indicazione area)
            if (memAddr.StartsWith("STD"))
            {
                // inizio verifica area memoria/parametro levando prima parte codice
                memAddr = memAddr.Replace("STD.", "");
            }
            // var di appoggio
            int cntAddr = 0;
            object outputVal = new object();
            // verifico se si tratta di lettura parametro... formato tipo STD.PAR.6711
            if (memAddr.StartsWith("PAR."))
            {
                // recupero parametro...
                int.TryParse(memAddr.Replace("PAR.", ""), out cntAddr);
                // processo parametro
                sw.Restart();
                FANUC_ref.F_RW_Param_Integer(false, cntAddr, 3, ref outputVal);
                if (utils.CRB("recTime"))
                {
                    TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("R{0}-PAR", 4), sw.ElapsedTicks);
                }
                // salvo valore
                answ = outputVal.ToString();
            }
            // 2022.05.23 gestione MACRO da testare (Jetco)
            else if (memAddr.StartsWith("MACRO."))
            {
                double macroVal = 0;
                // recupero parametro...
                int.TryParse(memAddr.Replace("MACRO.", ""), out cntAddr);
                lgTrace($"MACRO | Read 01 | memAddr: {memAddr} | idx: {cntAddr} | macroVal: {macroVal}");
                // processo parametro
                sw.Restart();
                FANUC_ref.F_Read_macro(cntAddr, ref macroVal);
                if (utils.CRB("recTime"))
                {
                    TimingData.addResult(IOBConfFull.General.CodIOB, "R-MACRO", sw.ElapsedTicks);
                }
                // salvo valore
                answ = macroVal.ToString();
                lgTrace($"MACRO | Read 02 | memAddr: {memAddr} | idx: {cntAddr} | macroVal: {macroVal}");
            }
            // altrimenti se legge da area memoria specifica leggo da li... formto tipo STD.D.1604.DW
            else
            {
                memAddressFanuc areaCounter = new memAddressFanuc(memAddr);

                if (isVerboseLog)
                {
                    lgInfo("getValByParam [0] area memoria: {0}.{1}.{2}", areaCounter.mType, areaCounter.mPos, areaCounter.vType);
                }

                // leggo!
                sw.Restart();
                // switch x tipo dati --> tipo lettura... e salvo ultimo conteggio rilevato
                switch (areaCounter.vType)
                {
                    case "B":
                        byte valB = 0;
                        FANUC_ref.F_RW_Byte(false, areaCounter.mType, areaCounter.mPos, ref valB);
                        outputVal = valB;
                        break;

                    case "D":
                        ushort valW = 0;
                        FANUC_ref.F_RW_Word(false, areaCounter.mType, areaCounter.mPos, ref valW);
                        outputVal = valW;
                        break;

                    case "DW":
                        uint valDW = 0;
                        FANUC_ref.F_RW_DWord(false, areaCounter.mType, areaCounter.mPos, ref valDW);
                        if (isVerboseLog)
                        {
                            lgInfo("[1] valDW PAR: {0}", valDW);
                        }

                        outputVal = valDW;
                        if (isVerboseLog)
                        {
                            lgInfo("[2] outputVal PAR: {0}", outputVal);
                        }

                        break;

                    default:
                        break;
                }
                if (utils.CRB("recTime"))
                {
                    TimingData.addResult(IOBConfFull.General.CodIOB, string.Format("R-{0}.{1}.{2}", areaCounter.mType, areaCounter.mPos, areaCounter.vType), sw.ElapsedTicks);
                }

                // salvo...
                answ = outputVal.ToString();
                if (isVerboseLog)
                {
                    lgInfo($"[3] Mem letta: {memAddr} | {answ}");
                }
            }
            sw.Stop();
            return answ;
        }

        private void tryIncreaseErrorComm(string topic)
        {
            lgTrace($"[Thread: {Thread.CurrentThread.ManagedThreadId}] | numErroriCheck: {numErroriCheck}");
            numErroriCheck++;
            parentForm.ChangeErrorDelay(topic, numErroriCheck, 5);
        }

        private void tryReduceErrorCount(string topic)
        {
            numErroriCheck -= 1;
            if (numErroriCheck >= 0)
            {
                lgTrace($"[Thread: {Thread.CurrentThread.ManagedThreadId}] | numErroriCheck: {numErroriCheck}");
            }
            numErroriCheck = numErroriCheck < 0 ? 0 : numErroriCheck;
            parentForm.ChangeErrorDelay(topic, numErroriCheck, -5);
        }

        #endregion Private Methods
    }
}