using IOB_UT_NEXT;
using IOB_UT_NEXT.Config;
using MapoSDK;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;

namespace IOB_WIN_KAWASAKI.Iob
{
    public class Kawasaki : Iob.GenericNext, IDisposable
    {
        #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 Kawasaki(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
        {
            // gestione invio ritardato contapezzi
            
            lastPzCountSend = DateTime.Now;
            lastWarnODL = DateTime.Now;
            // init connessione
            setConnection();

            // test completo funzionalità kawasaki, da TOGLIERE in prod...
#if DEBUG
            kawasakiFullTest();
#endif
        }

        #endregion Public Constructors

        #region Public Enums

        /// <summary>
        /// Enum segnali macchina ulteriori (ByteSignals)
        /// </summary>
        [Flags]
        public enum bitSignals
        {
            NONE = 0,
            TRANSF_AUTO = 1 << 0,
            PUNZ_AUTO = 1 << 1,
            BARR_TAV_RIPR = 1 << 2,
            ARIA_OK = 1 << 3,
            CONS_TRANS_OK = 1 << 4,
            TAV_A = 1 << 5,
            TAV_B = 1 << 6,
            RICH_ACCESSO = 1 << 7
        }

        /// <summary>
        /// Enum segnali status macchina (ByteStatus) come flag
        /// </summary>
        [Flags]
        public enum bitStatus
        {
            NONE = 0,
            POWER_ON = 1 << 0,
            AUTO = 1 << 1,
            RUN = 1 << 2,
            ERROR = 1 << 3,
            ALARM = 1 << 4,
            EMERG_OK = 1 << 5,
            DOOR_CLOSED = 1 << 6,
            READY_LOAD = 1 << 7
        }

        #endregion Public Enums

        #region Public Methods

        /// <summary>
        /// Metodo dispose x il currPLC contenuto
        /// </summary>
        public void Dispose()
        {
            KAWASAKI_ref.disconnect();
            KAWASAKI_ref.Dispose();
        }

        /// <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>();
            bool taskOk = false;
            string taskVal = "";

            // cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4
            foreach (var item in task2exe)
            {
                taskOk = false;
                taskVal = "";
                // converto richiesta in enum...
                taskType tName = taskType.nihil;
                Enum.TryParse(item.Key, out tName);
                // controllo sulla KEY
                switch (tName)
                {
                    case taskType.nihil:
                    case taskType.fixStopSetup:
                    case taskType.setArt:
                    case taskType.setComm:
                    case taskType.setProg:
                    case taskType.sendWatchDogMes2Plc:
                        taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
                        break;

                    case taskType.forceResetPzCount:
                        // reset contapezzi inizio setup
                        taskOk = resetContapezziPLC(codTav);
                        taskVal = taskOk ? "FORCE RESET PZ COUNT" : "FORCE RESET DISABLED | NO EXEC";
                        break;

                    case taskType.forceSetPzCount:
                        // reset contapezzi inizio setup
                        int newPzCount = 0;
                        int.TryParse(item.Value, out newPzCount);
                        if (newPzCount >= 0)
                        {
                            taskOk = setcontapezziPLC(newPzCount, codTav);
                            taskVal = taskOk ? $"FORCE SET PZ COUNT TO {newPzCount}" : $"FORCE SET PZ COUNT TO {newPzCount} | NO EXEC";
                        }
                        else
                        {
                            taskVal = $"ERROR IN FORCE SET PZ COUNT TO {newPzCount}";
                        }
                        break;

                    case taskType.startSetup:
                        // reset contapezzi inizio setup
                        taskOk = resetContapezziPLC(codTav);
                        taskVal = taskOk ? "RESET: SETUP START" : "PZ RESET DISABLED | NO EXEC";
                        break;

                    case taskType.stopSetup:
                        // reset contapezzi fine setup // reset contapezzi fine setup SE
                        // ESPLICITAMENTE IMPOSTATO
                        if (getOptPar("ENABLE_PZ_RESET_stopSetup") == "TRUE")
                        {
                            resetContapezziPLC(codTav);
                        }
                        taskVal = taskOk ? "RESET: SETUP END" : "PZ RESET DISABLED | NO EXEC";
                        break;

                    default:
                        taskVal = "SKIPPED | NO EXEC";
                        break;
                }
                taskDone.Add(item.Key, taskVal);
            }

            return taskDone;
        }

        /// <summary>
        /// Recupero dati dinamici...
        /// </summary>
        public override Dictionary<string, string> getDynData()
        {
            // valore non presente in vers default... se gestito fare override
            Dictionary<string, string> outVal = new Dictionary<string, string>();
            outVal.Add("LAST_MISS", lastMissRobot);
            outVal.Add("LAST_TC", lastTC.ToString());
            outVal.Add("NUM_PZ_PREL", pzPrelevati.ToString());
            outVal.Add("NUM_PZ_LAV", pzCounter.ToString());
            outVal.Add("CURR_SIGNALS", currBitmapSignals.ToString());
            return outVal;
        }

        /// <summary>
        /// Recupero programma in lavorazione
        /// </summary>
        /// <returns></returns>
        public override string getPrgName()
        {
            // valore non presente in vers default... se gestito fare override
            string prgName = "";
            return prgName;
        }

        /// <summary>
        /// Recupero info sistema generiche
        /// </summary>
        /// <returns></returns>
        public override Dictionary<string, string> getSysInfo()
        {
            // valore non presente in vers default... se gestito fare override
            Dictionary<string, string> outVal = new Dictionary<string, string>();
            outVal.Add("MACHINE", machineName);
            return outVal;
        }

        /// <summary>
        /// Effettua vero processing contapezzi
        /// </summary>
        public override void processContapezzi()
        {
            if (utils.CRB("enableContapezzi"))
            {
                try
                {
                    // hard coded... !!!FARE!!! rivedere megio conf
                    contapezziPLC = pzCounter;
                    // verifico quale modalità sia richiesta: STD (6711) oppure BIT (Custom, con
                    // indicazione area)
                    if (!string.IsNullOrEmpty(IOBConfFull.Device.PzCountMode))
                    {
#if false
            string memAddr = IOBConfFull.Device.PzCountMode;
            if (memAddr.StartsWith("STD"))
            {
              // inizio verifica area memoria/parametro levando prima parte codice
              memAddr = memAddr.Replace("STD.", "");
              object outputVal = new object();
              // verifico se si tratta di lettura area DB... formato tipo STD.DB700.DBB22.W
              if (memAddr.StartsWith("DB"))
              {
                memAreaSiemens areaCounter = new memAreaSiemens(memAddr);

                 if (isVerboseLog)
                {
                  lgInfo("[0] area memoria: {1}.{2}.{3}", memAddr, areaCounter.DbNum, areaCounter.indiceMem, areaCounter.tipoMem);
                }
                // copio da blocco già letto... con switch x tipo dati --> tipo lettura... e salvo
                // ultimo conteggio rilevato
                switch (areaCounter.tipoMem)
                {
                  case "B":
                    byte valB = RawInput[areaCounter.indiceMem];
                    outputVal = valB;
                    break;

                  case "W":
                    ushort valW = S7.Net.Types.Word.FromByteArray(RawInput.Skip(areaCounter.indiceMem).Take(2).ToArray());
                    outputVal = valW;
                    break;

                  case "DW":
                    uint valDW = S7.Net.Types.Word.FromByteArray(RawInput.Skip(areaCounter.indiceMem).Take(4).ToArray());
                    outputVal = valDW;

                    break;

                  default:
                    break;
                }
                // salvo...
                Int32.TryParse(outputVal.ToString(), out contapezziPLC);
                 if (isVerboseLog)
                {
                  lgInfo("[2] outputVal contapezzi: {0}", outputVal);
                  lgInfo("[3] contapezziPLC contapezzi: {0}", contapezziPLC);
                }
              }
              stopwatch.Stop();
            }
#endif
                    }
                }
                catch (Exception exc)
                {
                    lgError(exc, "Errore in contapezzi KAWASAKI");
                }
            }
        }

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

        /// <summary>
        /// Effettua lettura semafori principale <paramref name="currDispData">Parametri da
        /// aggiornare x display in form</paramref>
        /// </summary>
        public override void readSemafori(ref newDisplayData currDispData)
        {
            //base.readSemafori(ref currDispData);
            try
            {
                if (verboseLog)
                {
                    lgInfo("inizio read semafori");
                }
                currDispData.semIn = Semaforo.SV;

                // effettuo TUTTE le letture
                threadOk = commThreadOk;
                cStatus = currBitmapStatus;
                cSignals = currBitmapSignals;
                contapezziPLC = pzCounter;
                // decodifica e gestione
                decodeToBaseBitmap();
                decodeOtherData();
                reportRawInput(ref currDispData);
                // se trova ok thread o almeno kawasaki connesso --> aggiorno ultima lettura
                if (threadOk || KAWASAKI_ref.IsConnected)
                {
                    lastReadPLC = DateTime.Now;
                }
            }
            catch
            {
                currDispData.semIn = Semaforo.SR;
            }
        }

        /// <summary>
        /// Effettua reset del contapezzi
        /// </summary>
        /// <returns></returns>
        public override bool resetContapezziPLC(string codTav)
        {
            // impiego override metodo set...
            bool answ = setcontapezziPLC(0, codTav);
#if false
            bool answ = false;
            // ...SE abilitato da conf IOB
            if (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET") == "TRUE")
            {
                // scrivo valore 0 x il contapezzi
                try
                {
                    pzCounter = 0;
                }
                catch (Exception exc)
                {
                    lgError(exc, "Errore in RESET contapezzi KAWASAKI");
                    connectionOk = false;
                }
                answ = true;
            }
            else
            {
                lgError("Impossibile effettuare RESET contapezzi KAWASAKI, mancanza parametro OPT:ENABLE_PZ_RESET");
            }
#endif
            return answ;
        }

        /// <summary>
        /// Effettua IMPOSTAZIONE FORZATA del contapezzi
        /// </summary>
        /// <returns></returns>
        public override bool setcontapezziPLC(int newPzCount, string codTav)
        {
            bool answ = false;
            // ...SE abilitato da conf IOB
            if (getOptPar("ENABLE_PZ_RESET") == "TRUE")
            {
                // scrivo valore richiesto (0 tipicamente) x il contapezzi
                try
                {
                    pzCounter = newPzCount;
                }
                catch (Exception exc)
                {
                    lgError(exc, "Errore in SET contapezzi KAWASAKI");
                    connectionOk = false;
                }
                answ = true;
            }
            else
            {
                lgError("Impossibile effettuare SET contapezzi KAWASAKI, mancanza parametro OPT:ENABLE_PZ_RESET");
            }
            return answ;
        }

        /// <summary>
        /// Override connessione
        /// </summary>
        public override void tryConnect()
        {
            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("KAWASAKI: ConnKO - tryConnect");
                    }
                    // in primis salvo data ping...
                    lastPING = DateTime.Now;
                    // se passa il ping faccio il resto...
                    if (testPingMachine == IPStatus.Success)
                    {
                        string szStatusConnection = "";
                        try
                        {
                            // ora provo connessione...
                            parentForm.commPlcActive = true;
                            string connPar = $"TCP {IOBConfFull.Device.Connect.IpAddr}";
                            KAWASAKI_ref.connect(connPar);
                            parentForm.commPlcActive = false;
                            lgInfo("szStatusConnection: " + KAWASAKI_ref.IsConnected);
                            connectionOk = true;
                            // verifico se sta girando
                            if (connectionOk)
                            {
                                queueInEnabCurr = true;
                                if (adpRunning)
                                {
                                    lgInfo("Connessione OK");
                                }
                            }
                            else
                            {
                                lgError("Impossibile procedere, connessione mancante...");
                            }
                        }
                        catch (Exception exc)
                        {
                            lgFatal($"Errore nella connessione all'adapter KAWASAKI: {szStatusConnection}{Environment.NewLine}{exc}");
                            connectionOk = false;
                            lgInfo($"Eccezione in TryConnect, Adapter KAWASAKI NON running, pausa di {utils.CRI("waitRecMSec")} msec prima di ulteriori tentativi di riconnessione");
                        }
                    }
                    else
                    {
                        // loggo no risposta ping ...
                        connectionOk = false;
                        needRefresh = true;
                        if (verboseLog || periodicLog)
                        {
                            lgInfo($"Attenzione: KAWASAKI controllo PING fallito per IP {IOBConfFull.Device.Connect.PingIpAddr}");
                        }
                    }
                }
            }
            // se non è ancora connesso faccio processing memoria caso disconnesso...
            if (!connectionOk)
            {
                // processo semafori ed invio...
                processMemoryDiscon();
            }
        }

        /// <summary>
        /// Override disconnessione
        /// </summary>
        public override void tryDisconnect()
        {
            if (connectionOk)
            {
                connectionOk = false;
                string szStatusConnection = "";
                try
                {
                    KAWASAKI_ref.disconnect();
                    lgInfo(szStatusConnection);
                    lgInfo("Effettuata disconnessione adapter KAWASAKI!");
                }
                catch (Exception exc)
                {
                    lgFatal(exc, "Errore nella disconnessione dall'adapter KAWASAKI");
                }
            }
            else
            {
                lgError("IMPOSSIBILE effettuare disconnessione KAWASAKI: Connessione non disponibile...");
            }
            queueInEnabCurr = false;
            needRefresh = true;
        }

        #endregion Public Methods

        #region Protected Fields

        /// <summary>
        /// Comando inviato al robot
        /// </summary>
        protected string comando;

        /// <summary>
        /// Variabile SIGNALS corrente (8bit INT)
        /// </summary>
        protected string cSignals;

        /// <summary>
        /// Variabile STATUS corrente (8bit INT)
        /// </summary>
        protected string cStatus;

        /// <summary>
        /// Oggetto MAIN x connessione KAWASAKI
        /// </summary>
        protected KRcc.Commu KAWASAKI_ref;

        /// <summary>
        /// Ultima missione svolta da robot
        /// </summary>
        protected string lastMissRobot;

        /// <summary>
        /// Ultimo TC registrato da robot
        /// </summary>
        protected decimal lastRecTC;

        /// <summary>
        /// Nome e seriale macchina
        /// </summary>
        protected string macName = "";

        /// <summary>
        /// Num pezzi prelevati
        /// </summary>
        protected int numPzPrel;

        /// <summary>
        /// Array delle risposte dal controllo KAWASAKI
        /// </summary>
        protected System.Collections.ArrayList resDataArray;

        /// <summary>
        /// Variabile verifica thread comunicazione
        /// </summary>
        protected bool threadOk = false;

        #endregion Protected Fields

        #region Protected Properties

        /// <summary>
        /// Verifica se il thread 4 di comm sia attivo
        /// </summary>
        protected bool commThreadOk
        {
            get
            {
                bool answ = false;
                if (KAWASAKI_ref.IsConnected)
                {
                    int retVal = 0;
                     resDataArray = KAWASAKI_ref.command("TYPE TASK (1004)", 3000);    // thread 4 ok      --> " 1\r\n"
                    int.TryParse(resDataArray[1].ToString().Replace("\n", "").Replace("\r", ""), out retVal);
                    answ = (retVal == 1);
                }
                return answ;
            }
        }

        /// <summary>
        /// Restituisce SEGNALI macchina nel formato:
        /// B0: Transfer in auto
        /// B1: Punzonatrice in AUTO
        /// B2: Barriera tavola ripristinata
        /// B3: Aria in linea OK
        /// B4: Console Transfer in fuori ingombro
        /// B5: TAV A
        /// B6: TAV B
        /// B7: Richiesta accesso attiva
        /// </summary>
        protected string currBitmapSignals
        {
            get
            {
                string answ = "";
                if (KAWASAKI_ref.IsConnected)
                {
                    resDataArray = KAWASAKI_ref.command("TYPE $signal", 3000);    // segnali              --> "1|0|1|1|1|0|0|0\r\n"
                    answ = resDataArray[1].ToString().Replace("\n", "").Replace("\r", "").Replace("|", "");
#if false
          // reverse stringa (B0 portato a sx)
          szBitmap = utils.reverseStr(szBitmap);
          // ora converto bitmap string in INT
          answ = Convert.ToInt32(szBitmap, 2);
#endif
                }
                return answ;
            }
        }

        /// <summary>
        /// Restituisce stato macchina nel formato (da sx a dx):
        /// B0: POWER_ON
        /// B1: AUTO
        /// B2: RUN
        /// B3: ERROR
        /// B4: ALLARME
        /// B5: EMERGENZA OK
        /// B6: PORTA CHIUSA
        /// B7: PRONTO AL LOAD
        /// </summary>
        protected string currBitmapStatus
        {
            get
            {
                string answ = "";
                if (KAWASAKI_ref.IsConnected)
                {
                    resDataArray = KAWASAKI_ref.command("TYPE $status", 3000);    // status        --> "0|0|1|0|0|1|0|0\r\n"
                    answ = resDataArray[1].ToString().Replace("\n", "").Replace("\r", "").Replace("|", "");
#if false
          // reverse stringa (B0 portato a sx)
          szBitmap = utils.reverseStr(szBitmap);
          // ora converto bitmap string in INT
          answ = Convert.ToInt32(szBitmap, 2);
#endif
                }
                return answ;
            }
        }

        /// <summary>
        /// Recupera ultima missione svolta
        /// </summary>
        protected string lastMission
        {
            get
            {
                string answ = "";
                if (KAWASAKI_ref.IsConnected)
                {
                    resDataArray = KAWASAKI_ref.command("TYPE $exe", 3000);    // missione              -->  "\r\n" (vuoto, no missione)
                    answ = resDataArray[1].ToString().Replace("\n", "").Replace("\r", "");
                }
                return answ;
            }
        }

        /// <summary>
        /// Oggetto per lettura ULTIMO TC rilevato
        /// </summary>
        protected decimal lastTC
        {
            get
            {
                decimal answ = 0;
                if (KAWASAKI_ref.IsConnected)
                {
                    resDataArray = KAWASAKI_ref.command("TYPE r_tempo", 3000);    // ultimo TCiclo rilevato              -->  " 349.19\r\n"
                    decimal.TryParse(resDataArray[1].ToString().Replace("\n", "").Replace("\r", "").Replace(".", ","), out answ);
                }
                return answ;
            }
        }

        /// <summary>
        /// Recupera nome e seriale macchina
        /// </summary>
        protected string machineName
        {
            get
            {
                string answ = "";
                if (KAWASAKI_ref.IsConnected)
                {
                    resDataArray = KAWASAKI_ref.command("TYPE $id_true", 3000);    // id macchina   --> "BX100N-B001 Sn. 2366\r\n"
                    answ = resDataArray[1].ToString().Replace("\n", "").Replace("\r", "");
                }
                return answ;
            }
        }

        /// <summary>
        /// Oggetto per lettura/scrittura counter pezzi robot
        /// </summary>
        protected int pzCounter
        {
            get
            {
                int answ = 0;
                if (KAWASAKI_ref.IsConnected)
                {
                    resDataArray = KAWASAKI_ref.command("TYPE i_cicli", 3000);    // num cicli depositati/fatti              --> " 0\r\n"
                    int.TryParse(resDataArray[1].ToString().Replace("\n", "").Replace("\r", ""), out answ);
                }
                return answ;
            }
            set
            {
                if (KAWASAKI_ref.IsConnected)
                {
                    comando = string.Format("i_cicli={0}", value);
                    // scrivo valore cicli
                    resDataArray = KAWASAKI_ref.command(comando, 3000);    // imposto cicli depositati/fatti   --> " 0\r\n"
                }
            }
        }

        /// <summary>
        /// Oggetto per lettura counter pezzi PRELEVATI
        /// </summary>
        protected int pzPrelevati
        {
            get
            {
                int answ = 0;
                if (KAWASAKI_ref.IsConnected)
                {
                    resDataArray = KAWASAKI_ref.command("TYPE i_prelevati", 3000);    // num pz prelevati              --> 		"0\r\n"
                    int.TryParse(resDataArray[1].ToString().Replace("\n", "").Replace("\r", ""), out answ);
                }
                return answ;
            }
        }

        #endregion Protected Properties

        #region Protected Methods

        /// <summary>
        /// Imposto connessione
        /// </summary>
        protected virtual void setConnection()
        {
            // Creo oggetto connessione NC
            parentForm.commPlcActive = true;
            lgInfoStartup($"Start init Adapter KAWASAKI all'IP {IOBConfFull.Device.Connect.IpAddr} | --> IOB {IOBConfFull.General.CodIOB}");

            // SE è necessario refresh...
            if (needRefresh)
            {
                lgInfoStartup("Refreshing connection...");
                // ora tento avvio PLC... SE PING OK...
                if (testPingMachine == IPStatus.Success)
                {
                    try
                    {
                        string connPar = $"TCP {IOBConfFull.Device.Connect.IpAddr}";
                        KAWASAKI_ref = new KRcc.Commu(connPar);
                        // disconnetto e connetto...
                        if (isVerboseLog)
                        {
                            lgInfo("KAWASAKI: tryDisconnect");
                        }

                        lgInfo("End init Adapter KAWASAKI");
                        if (isVerboseLog)
                        {
                            lgInfo("KAWASAKI CONNESSIONE AVVENUTA");
                        }
                    }
                    catch (Exception exc)
                    {
                        lgError(exc, "Errore in INIT KAWASAKI Commu");
                    }
                    needRefresh = false;
                }
                parentForm.commPlcActive = false;
                // gestione pzCounter
                if (utils.CRB("enableContapezzi"))
                {
                    lgInfo("KAWASAKI: 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 KAWASAKI: pzCntReload(true)");
                                pzCntReload(true);
                                // refresh associazione Macchina - IOB
                                SendM2IOB();
                                // invio altri dati accessori...
                                SendMachineConf();
                                // per adesso imposto lettura fanuc == contapezzi (poi farà vera lettura...)
                                contapezziPLC = contapezziIOB;
                            }
                            else
                            {
                                contapezziIOB = 0;
                                lgInfo("Contapezzi STD disabilitato: modalità {0}", IOBConfFull.Device.PzCountMode);
                            }
                        }
                        else
                        {
                            contapezziIOB = 0;
                            lgInfo("Parametro mancante PZCOUNT_MODE");
                        }
                    }
                    catch (Exception exc)
                    {
                        lgError(exc, "Errore in contapezzi KAWASAKI");
                    }
                }
            }
        }

        #endregion Protected Methods

        #region Private Fields

        /// <summary>
        /// LookUpTable di decodifica da CNC a segnali tipo bitmap MAPO
        /// </summary>
        private Dictionary<string, string> signLUT = new Dictionary<string, string>();

        #endregion Private Fields

        #region Private Methods

        /// <summary>
        /// Decodifica il resto dell'area x i dati accessori (allarmi, ...)
        /// </summary>
        private void decodeOtherData()
        {
            if (verboseLog)
            {
            }
        }

        /// <summary>
        /// Effettua decodifica aree memoria alla bitmap usata x MAPO
        /// </summary>
        private void decodeToBaseBitmap()
        {
            // init a zero...
            B_input = 0;
            /* -----------------------------------------------------
             * bitmap MAPO
             * B0: POWER_ON
             * B1: RUN
             * B2: pzCount
             * B3: allarme
             * B4: manuale
             * B5: emergenza
             * B6: error prog
             * B7: auto mode
            ----------------------------------------------------- */
            // bit 0 (poweron) imposto a 1 SE connected...
            B_input = KAWASAKI_ref.IsConnected ? 1 : 0;
            // RUN
            if (cStatus[2] == '1')
            {
                B_input += (1 << 1);
            }
            // ERROR prog/macchina
            if (cStatus[3] == '1')
            {
                B_input += (1 << 6);
            }
            // allarme
            if (cStatus[4] == '1')
            {
                B_input += (1 << 3);
            }
            // Automatico (porta chiusa)
            if (cStatus[6] == '1' || cStatus[1] == '1')
            {
                B_input += (1 << 4);
            }
            // NON EMERGENZA (1=armed, 0=triggered)
            if (cStatus[5] == '0')
            {
                B_input += (1 << 5);
            }
            // procedo SOLO SE è enabled IOB
            if (IobOnline)
            {
                try
                {
                    currODL = utils.callUrl(urlGetCurrODL);
                    // solo SE HO un ODL...
                    if (string.IsNullOrEmpty(currODL) || currODL == "0")
                    {
                        if (periodicLog)
                        {
                            lgInfo(string.Format("Kawasaki | Lettura ODL andata a vuoto: currODL: {0}", currODL));
                        }
                    }
                    else
                    {                   // se variato o scaduto timeout log...
                        if (periodicLog || (currIdxODL.ToString() != currODL))
                        {
                            lgInfo(string.Format("Kawasaki | Lettura ODL, currODL: {0} --> currIdxODL prec: {1}", currODL, currIdxODL));
                        }
                        // provo a salvare nuovo ODL
                        int.TryParse(currODL, out currIdxODL);
                    }
                }
                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($"Kawasaki | Lettura ODL non effettuata: IobOnline: {IobOnline} | currODL impostato a vuoto");
                }
            }
            if (!string.IsNullOrEmpty(currODL))
            {
                if (currODL != "0")
                {
                    // ora processo il contapezzi... 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 CNC/IOB: contapezziPLC: {contapezziPLC} | contapezziIOB: {contapezziIOB}");
                        }
                        // verifico se variato contapezzi... e se passato ritardo minimo...
                        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 KAWASAKI: {contapezziPLC} | contapezziIOB {contapezziIOB}");
                        }
                        else if (contapezziIOB > contapezziPLC)
                        {
                            lgInfo($"Contapezzi IOB > CNC --> NON INVIO (contapezziPLC: {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 KAWASAKI: contapezziPLC {contapezziPLC} | contapezziIOB {contapezziIOB} | risposta: {retVal}");
                            // rileggo il counter pezzi da server
                            pzCntReload(true);
                        }
                        // resetto timer...
                        lastPzCountSend = DateTime.Now;
                    }
                }
                else
                {
                    lgError("Attenzione non trovato ODL --> currODL = 0");
                }
            }
            else
            {
                if (DateTime.Now >= lastPzCountSend.AddMilliseconds(pzCountDelay))
                {
                    lgError($"Attenzione: mancanza ODL non procedo con gestione contapezzi. contapezziPLC KAWASAKI {contapezziPLC} | contapezziIOB {contapezziIOB}");
                    // resetto timer...
                    lastPzCountSend = DateTime.Now;
                }
            }

            // log opzionale!
            if (verboseLog)
            {
                lgInfo($"Trasformazione B_input: {B_input}");
            }
        }

        /// <summary>
        /// Test completo funzioni kawasaki
        /// </summary>
        private void kawasakiFullTest()
        {
            // faccio un try-catch di test vari...
            try
            {
                //string connPar = string.Format("TCP {0}", cIobConf.cncIpAddr);
                //KAWASAKI_ref = new KRcc.Commu(connPar);
                //KAWASAKI_ref = new KRcc.Commu("TCP 192.168.0.92");
                if (KAWASAKI_ref.IsConnected)
                {
                    // connect ok
                    resDataArray = KAWASAKI_ref.command("where", 3000);
                    // WHERE [RET] Console.WriteLine(resDataArray[1]); [0] = 0 [1] = JT1 JT2 JT3 JT4
                    // JT5 JT6
                    // -23.205 - 39.967 - 13.176 95.663 71.402 - 21.512 X[mm] Y[mm] Z[mm] O[deg]
                    // A[deg] T[deg]
                    // - 489.620 693.940 1564.733 92.612 131.285 7.482

                    threadOk = commThreadOk;
                    macName = machineName;
                    cStatus = currBitmapStatus;
                    cSignals = currBitmapSignals;
                    contapezziPLC = pzCounter;
                    numPzPrel = pzPrelevati;
                    lastRecTC = lastTC;
                    lastMissRobot = lastMission;
                }
                // test contapezzi
#if false
        // leggo i pezzi
        int currCount = pzCounter;
        // imposto a 100
        pzCounter = 100;
        // rileggo x verifica
        int newCount = pzCounter;
        // reimposto corretti
        pzCounter = currCount;
#endif
                // test gest programmi
#if true
                //saveProgram("prog", "default");
                sendProgram("prog", "default2");
#endif
            }
            catch (Exception e)
            {    // e.Message = "can't connect TCP/IP" or    //             "can't login"
                Console.WriteLine(e.Message);
            }
        }

        /// <summary>
        /// Effettua salvataggio del programma corrente
        /// </summary>
        /// <param name="progDir">Dir di riferimento</param>
        /// <param name="progName">Nome Programma (se mancasse *.as lo aggiunge)</param>
        private void saveProgram(string progDir = "prog", string progName = "current.as")
        {
            // path completo
            progName = progName.EndsWith(".as") ? progName : progName + ".as";
            string prgPath = progName;// string.Format("{0}/{1}", progDir, progName);
            if (KAWASAKI_ref.IsConnected)
            {
                // comandi x setup programma...
                resDataArray = KAWASAKI_ref.command("ferma", 3000);    // ferma processi (all)              -->       ""
                resDataArray = KAWASAKI_ref.command("togli", 3000);    // toglie processi da pronta esecuzione              -->       ""

                //resp = com.command("SAVE nome_del_file", 3000);    // salvataggio file... FORSE    -->        "\u0005\u0002Bnome_del_file.as\u0017"       --> verificare DOVE salva...

                int ret = KAWASAKI_ref.save(prgPath);  // SAVE current.as[RET]
                if (ret == 0)
                {
                    // Success
                }
                if (ret == -1)
                {
                    // Communication error
                }
                if (ret == -2)
                {
                    // Robot Controller error
                }
                if (ret == -3)
                {
                    // Internal error
                }
            }
        }

        /// <summary>
        /// Effettua invio del programma di alvorazione al ROBOT <param name="progDir">Dir di
        /// riferimento</param><param name="progName">Nome Programma (se mancasse *.as lo aggiunge)</param>
        /// </summary>
        private void sendProgram(string progDir = "prog", string progName = "default.as")
        {
            // path completo
            progName = progName.EndsWith(".as") ? progName : progName + ".as";
            string prgPath = progName;// string.Format("{0}/{1}", progDir, progName);

            // comandi x setup programma...
            resDataArray = KAWASAKI_ref.command("ferma", 3000);    // ferma processi (all)              -->       ""
            resDataArray = KAWASAKI_ref.command("togli", 3000);    // toglie processi da pronta esecuzione              -->       ""

            if (KAWASAKI_ref.IsConnected)
            {
                // carico...
                KAWASAKI_ref.asInquiry = delegate (string as_msg)
                {
                    Console.WriteLine(as_msg); if (as_msg.StartsWith("Are you sure ?"))
                    {
                        return "0\n"; //  0 [RET]
                    }

                    if (as_msg.StartsWith("Load data?"))
                    {
                        return "1\n";     //  1 [RET]
                    }

                    return null;
                };
                int retLoad = KAWASAKI_ref.load(prgPath, "/Q");   // LOAD/Q default.as[RET]
            }
        }

        #endregion Private Methods
    }
}