using CMSCncLib.CNC;
using MTC;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace MTC_Adapter
{
    public class AdapterOsai : AdapterGeneric
    {
        /// <summary>
        /// vettore valori in file interscambio
        /// </summary>
        public Dictionary<string, string> generalStatus;
        /// <summary>
        /// Contenuto valori maintenance data da PLC
        /// </summary>
        public otherData[] maintData;
        /// <summary>
        /// Contenuto valori status data da PLC
        /// </summary>
        public otherData[] statusData;
        /// <summary>
        /// Oggetto MAIN x connessione OSAI
        /// </summary>
        protected OSAI OSAI_ref;

        /// <summary>
        /// wrapper chiamata lettura/scrittura OSAI x BYTE...
        /// </summary>
        /// <param name="bWrite"></param>
        /// <param name="MemType"></param>
        /// <param name="memIndex"></param>
        /// <param name="memOrderStart">0/1 orimo o secondo byte della word...</param>
        /// <param name="MATRICE Value"></param>
        /// <returns></returns>
        public bool OsaiMemRW_Byte(bool bWrite, OSAI.MemTypeWord MemType, Int32 memIndex, Int32 memOrderStart, ref byte[] Value)
        {
            bool answ = false;
            if (OSAI_ref.Connected)
            {
                try
                {
                    answ = OSAI_ref.O_RW_Byte(bWrite, MemType, memIndex, memOrderStart, ref Value);
                }
                catch
                { }
            }
            return answ;
        }
        /// <summary>
        /// wrapper chiamata lettura/scrittura x WORD...
        /// </summary>
        /// <param name="bWrite"></param>
        /// <param name="MemType"></param>
        /// <param name="memIndex"></param>
        /// <param name="MATRICE Value"></param>
        /// <returns></returns>
        public bool OsaiMemRW_Word(bool bWrite, OSAI.MemTypeWord MemType, Int32 memIndex, ref ushort[] Value)
        {
            bool answ = false;
            if (OSAI_ref.Connected)
            {
                try
                {
                    answ = OSAI_ref.O_RW_Word(bWrite, MemType, memIndex, ref Value);
                }
                catch
                { }
            }
            return answ;
        }
        /// <summary>
        /// wrapper chiamata lettura/scrittura x DOUBLE-WORD...
        /// </summary>
        /// <param name="bWrite"></param>
        /// <param name="MemType"></param>
        /// <param name="memIndex"></param>
        /// <param name="MATRICE Value"></param>
        /// <returns></returns>
        public bool OsaiMemRW_DWord(bool bWrite, OSAI.MemTypeWord MemType, Int32 memIndex, ref uint[] Value)
        {
            bool answ = false;
            if (OSAI_ref.Connected)
            {
                try
                {
                    answ = OSAI_ref.O_RW_DWord(bWrite, MemType, memIndex, ref Value);
                }
                catch
                { }
            }
            return answ;
        }
        /// <summary>
        /// wrapper chiamata lettura/scrittura x Short...
        /// </summary>
        /// <param name="bWrite"></param>
        /// <param name="MemType"></param>
        /// <param name="memIndex"></param>
        /// <param name="MATRICE Value"></param>
        /// <returns></returns>
        public bool OsaiMemRW_Short(bool bWrite, OSAI.MemTypeWord MemType, Int32 memIndex, ref short[] Value)
        {
            bool answ = false;
            if (OSAI_ref.Connected)
            {
                try
                {
                    answ = OSAI_ref.O_RW_Short(bWrite, MemType, memIndex, ref Value);
                }
                catch
                { }
            }
            return answ;
        }
        /// <summary>
        /// wrapper chiamata lettura/scrittura x INT...
        /// </summary>
        /// <param name="bWrite"></param>
        /// <param name="MemType"></param>
        /// <param name="memIndex"></param>
        /// <param name="MATRICE Value"></param>
        /// <returns></returns>
        public bool OsaiMemRW_Integer(bool bWrite, OSAI.MemTypeWord MemType, Int32 memIndex, ref int[] Value)
        {
            bool answ = false;
            if (OSAI_ref.Connected)
            {
                try
                {
                    answ = OSAI_ref.O_RW_Integer(bWrite, MemType, memIndex, ref Value);
                }
                catch
                { }
            }
            return answ;
        }
        /// <summary>
        /// wrapper chiamata lettura/scrittura x Double...
        /// </summary>
        /// <param name="bWrite"></param>
        /// <param name="MemType"></param>
        /// <param name="memIndex"></param>
        /// <param name="MATRICE Value"></param>
        /// <returns></returns>
        public bool OsaiMemRW_Double(bool bWrite, OSAI.MemTypeDouble MemType, Int32 memIndex, ref double[] Value)
        {
            bool answ = false;
            if (OSAI_ref.Connected)
            {
                try
                {
                    answ = OSAI_ref.O_RW_Double(bWrite, MemType, memIndex, ref Value);
                }
                catch
                { }
            }
            return answ;
        }
        /// <summary>
        /// struttura dati OSAI x dati NC (pag 148)
        /// </summary>
        protected CMSCncLib.OPENcontrol.GETINFO1DATA allNcData;
        /// <summary>
        /// Vettore degli allarmi CNC attivi
        /// </summary>
        public CMSCncLib.OPENcontrol.MSGERROR allarmiCNC;
        /// <summary>
        /// estende l'init della classe base...
        /// </summary>
        /// <param name="caller"></param>
        /// <param name="adpConf"></param>
        public AdapterOsai(MainForm caller, AdapterConf adpConf) : base(caller, adpConf)
        {
            // !!!HARD CODED!!! aggiunto banco STATUS del 2 processo IN CODA... (4+1)DW=20byte x strobes...!!!
            Strobes = new byte[20];

            lg.Info("Start init Adapter OSAI all'IP {0}", utils.CRS("ipPLC"));

            Runtime.CreateNC(CNC.NcType.OSAI, utils.CRS("ipPLC"));

            // inizializzo posizioni assi...
            prevPosAxis = new double[adpConf.nAxis];
            prevDirAxis = new int[adpConf.nAxis];

            OSAI_ref = (OSAI)Runtime.NC;
            if (utils.CRB("verbose")) lg.Info(string.Format("Generato oggetto OSAI_ref da CMSCncLib:{0}{1}", Environment.NewLine, OSAI_ref.Descrizione));

            // disconnetto e connetto...
            if (utils.CRB("verbose")) lg.Info("OSAI: tryDisconnect");
            tryDisconnect();
            lg.Info("OSAI: tryConnect");
            tryConnect();
            lg.Info("End init Adapter OSAI");
        }
        /// <summary>
        /// Override disconnessione
        /// </summary>
        public override void tryDisconnect()
        {
            if (connectionOk)
            {
                string szStatusConnection = "";
                try
                {
                    OSAI_ref.Disconnect(ref szStatusConnection);
                    connectionOk = false;
                    lg.Info(szStatusConnection);
                    lg.Info("Effettuata disconnessione adapter OSAI!");
                }
                catch (Exception exc)
                {
                    lg.Fatal(exc, "Errore nella disconnessione dall'adapter OSAI");
                }
            }
            else
            {
                lg.Error("IMPOSSIBILE effettuare disconnessione: Connessione non disponibile...");
            }
        }
        /// <summary>
        /// Override connessione
        /// </summary>
        public override void tryConnect()
        {
            if (!connectionOk)
            {
                string szStatusConnection = "";
                try
                {
                    OSAI_ref.Connect(ref szStatusConnection);
                    lg.Info("szStatusConnection: " + szStatusConnection);
                    connectionOk = true;
                    // refresh stato allarmi!!!
                    if (connectionOk)
                    {
                        if (adpRunning)
                        {
                            // carico status allarmi (completo)
                            lg.Info("Inizio refresh completo stato allarmi...");
                            forceAlarmCheck();
                            lg.Info("Completato refresh completo stato allarmi!");
                        }
                        else
                        {
                            lg.Info("Connessione OK");
                        }
                    }
                    else
                    {
                        lg.Error("Impossibile procedere, connessione mancante...");
                    }
                }
                catch (Exception exc)
                {
                    lg.Fatal(string.Format("Errore nella connessione all'adapter OSAI: {0}{1}{2}", szStatusConnection, Environment.NewLine, exc));
                    connectionOk = false;
                    lg.Info(string.Format("Segnalo Adapter NON running, pausa di {0} msec prima di ulteriori tentativi di riconnessione", utils.CRI("waitRecMSec")));
                }
            }
        }
        /// <summary>
        /// Verifico connessione OSAI...
        /// </summary>
        /// <returns></returns>
        public override bool connectionOk
        {
            get
            {
                return OSAI_ref.Connected;
            }
        }
        /// <summary>
        /// Effettuo lettura dei 16 byte di strobe/status
        /// </summary>
        public override void getStrobeAndAckStatus()
        {
            base.getStrobeAndAckStatus();
            if (connectionOk)
            {
                // leggo TUTTO ack e strobe... in blocco di 16 WORD (32 byte) + altre 2 in coda...
                ushort[] MemBlock_A = new ushort[16];
                ushort[] MemBlock_B = new ushort[2];

                int memIndexA = 19018;
                int memIndexB = 19698;

                // NB: LEGGO SEMPRE a WORD 16bit (+ veloce...)

                // leggo blocco dati + grande...
                inizio = DateTime.Now;
                OsaiMemRW_Word(R, OSAI.MemTypeWord.MW_CODE, memIndexA, ref MemBlock_A);
                if (utils.CRB("recTime")) TimingData.addResult(string.Format("R-{0}-W4", MemBlock_A.Length * 2), DateTime.Now.Subtract(inizio).Ticks);

                // leggo blocco dati + piccolo
                inizio = DateTime.Now;
                OsaiMemRW_Word(R, OSAI.MemTypeWord.MW_CODE, memIndexB, ref MemBlock_B);
                if (utils.CRB("recTime")) TimingData.addResult(string.Format("R-{0}-W1", MemBlock_B.Length * 2), DateTime.Now.Subtract(inizio).Ticks);

                // suddivido blocco a blocco... da file di conf x OSAI!!!
                // primi 12 byte ad ACK
                Buffer.BlockCopy(MemBlock_A, 0, Acknowl, 0, Acknowl.Length - 4);
                // altri 4 byte ad ACK blocco secondario
                Buffer.BlockCopy(MemBlock_B, 0, Acknowl, Acknowl.Length - 4, 4);
                // tutti i 16+4 byte a strobe/status
                Buffer.BlockCopy(MemBlock_A, Acknowl.Length - 4, Strobes, 0, Strobes.Length);
            }
            else
            {
                lg.Error("Errore connessione mancante in getStrobeAndAckStatus");
            }
        }
        /// <summary>
        /// processing strobe!
        /// </summary>
        public override void processStrobe()
        {
            base.processStrobe();

            // inizializzo userAction
            string UserAction = "";
            // oggetti "accessori" x processing (1 byte di strobe x ogni path)
            StFlag8 currStrobe;
            StFlag8 currAck;

            // processo ora i dati dei path... di sicuro il primo
            currStrobe = (StFlag8)(Strobes[4]); // 5° byte
            currAck = (StFlag8)(Acknowl[4]); // 5° byte
            procPathStrobes(0, currStrobe, currAck, 19100, 19020, ref UserAction);

            //...e se c'è pure il secondo...
            if (currAdpConf.nPath > 1)
            {
                currStrobe = (StFlag8)(Strobes[6]); // 7° byte
                currAck = (StFlag8)(Acknowl[6]); // 7° byte
                procPathStrobes(1, currStrobe, currAck, 19126, 19021, ref UserAction);
            }

            // 2017.01.16 INVIO vettore azioni (1 o +)... SE CE NE SONO!
            if (UserAction.Trim() != "")
            {
                mUserAction.ForceChanged();
                mUserAction.Value = UserAction.Trim();
            }

            // verifico strobe dell'auto-test
            currStrobe = (StFlag8)(Strobes[7]); // 8° byte
            currAck = (StFlag8)(Acknowl[7]); // 8° byte
            processTestStrobe(currStrobe, currAck, 19021, 1);

            // gestione bit di watchdog... sulal DWord successiva
            sendWatchDog();
        }
        /// <summary>
        /// Processa strobe x un dato path
        /// </summary>
        /// <param name="idxPath">path corrente</param>
        /// <param name="currStrobe">Byte di strobe del path corrente</param>
        /// <param name="currAck">Byte di strobe del path corrente</param>
        /// <param name="memIndexMST">indice x lettura memoria MST del path</param>
        /// <param name="memIndexAck">indice x scrittura ACK x path</param>
        /// <param name="UserAction">stringa COMPLESSIVA azioni utente</param>
        private void procPathStrobes(int idxPath, StFlag8 currStrobe, StFlag8 currAck, int memIndexMST, int memIndexAck, ref string UserAction)
        {
            // byte di strobe e di acknowledge... inizializzo!!!
            byte[] currACK_DW = new byte[1];
            // altre variabili
            int bitNum = 0;
            inizio = DateTime.Now;
            // incomincio vera e propria gestione...
            try
            {
                // controllo TUTTI i flag: se ce ne sono di alzati DEVO processare...
                if (currStrobe != StFlag8.NONE)
                {
                    // se ho un M/S/T leggo area...
                    if (currStrobe.HasFlag(StFlag8.B0) || currStrobe.HasFlag(StFlag8.B1) || currStrobe.HasFlag(StFlag8.B2))
                    {

                        // blocco memoria x lettura TUTTI i dati di buffer M/S/T: 26 short(16bit) x (12+7+7) aree (attenzione: secondo set di 2 bit è VUOTO...)
                        ushort[] MemBlock_W = new ushort[26];

                        // leggo tutto!!!
                        inizio = DateTime.Now;
                        OsaiMemRW_Word(R, OSAI.MemTypeWord.MW_CODE, memIndexMST, ref MemBlock_W);
                        if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-STRB_DW1-P{1:00}", MemBlock_W.Length, idxPath), DateTime.Now.Subtract(inizio).Ticks);

                        // converto a byte x compatibilità...
                        byte[] MemBlock = new byte[MemBlock_W.Length * 2];
                        Buffer.BlockCopy(MemBlock_W, 0, MemBlock, 0, MemBlock.Length);

                        // check COD_M
                        bitNum = 0;
                        gestStrobeCodMST(currStrobe, bitNum, ref currACK_DW, 0, MemBlock, "M");

                        // check COD_S
                        bitNum = 1;
                        gestStrobeCodMST(currStrobe, bitNum, ref currACK_DW, 12, MemBlock, "S");

                        // check COD_T
                        bitNum = 2;
                        gestStrobeCodMST(currStrobe, bitNum, ref currACK_DW, 19, MemBlock, "T");

                    }

                    // check FILE DATI MODIFICATO: ricaricare...
                    bitNum = 3;
                    if (STRB_DW1.HasFlag((StFlag32)Math.Pow(2, bitNum)))
                    {
                        lg.Info("Notifica file modificato");
                        try
                        {
                            // reload dati da file...
                            reloadDataFromFile();

                            // memorizzo allarme nel vettore ack....
                            currACK_DW = utils.setBitOnStFlag(currACK_DW, true, bitNum);
                        }
                        catch
                        {
                            lg.Info("Errore lettura dati da file");
                        }
                    }

                    // AREA strobe USER ACTION
                    // chiamato Start...
                    bitNum = 4;
                    gestStrobeUserAction(bitNum, ref currACK_DW, ref UserAction, string.Format(" (P{0:00} START) ", idxPath));
                    // chiamato Stop...
                    bitNum = 5;
                    gestStrobeUserAction(bitNum, ref currACK_DW, ref UserAction, string.Format(" (P{0:00} STOP) ", idxPath));
                    // chiamato Reset...
                    bitNum = 6;
                    gestStrobeUserAction(bitNum, ref currACK_DW, ref UserAction, string.Format(" (P{0:00} RESET) ", idxPath));

                }
                else
                {
                    // inizializzo 4 byte a zero!!!
                    currACK_DW = new byte[1];
                }
            }
            catch (Exception exc)
            {
                lg.Error(string.Format("Errore in strobe:{0}{1}", Environment.NewLine, exc));
            }

            // scrivo update ad ack SE VARIATO!!!
            if (currAck != (StFlag8)currACK_DW[0])
            {
                inizio = DateTime.Now;
                OsaiMemRW_Byte(W, OSAI.MemTypeWord.MW_CODE, memIndexAck, 0, ref currACK_DW);
                if (utils.CRB("recTime")) TimingData.addResult(string.Format("W{0}-ACK_DW0", currACK_DW.Length), DateTime.Now.Subtract(inizio).Ticks);
            }
        }
        /// <summary>
        /// Verifica strobe autotest
        /// </summary>
        /// <param name="currStrobe"></param>
        /// <param name="currAck"></param>
        /// <param name="memIndexAck"></param>
        private void processTestStrobe(StFlag8 currStrobe, StFlag8 currAck, int memIndexAck, int memOrderStart)
        {
            // byte di strobe e di acknowledge... inizializzo!!!
            byte[] currACK_DW = new byte[1];
            string TestingData = "";
            // INIZIO TEST...
            int bitNum = 6;
            if (currStrobe.HasFlag((StFlag8)Math.Pow(2, bitNum)))
            {
                // formatto stringa risultato
                TestingData = string.Format("START TEST{0}", getTestData(utils.CRS("testCharSep")));

                // memorizzo allarme nel vettore ack....
                currACK_DW = utils.setBitOnStFlag(currACK_DW, true, bitNum);
            }
            // FINE TEST...
            bitNum = 7;
            if (currStrobe.HasFlag((StFlag8)Math.Pow(2, bitNum)))
            {
                // formatto stringa risultato
                TestingData = string.Format("STOP TEST{0}", getTestData(utils.CRS("testCharSep")));

                // memorizzo allarme nel vettore ack....
                currACK_DW = utils.setBitOnStFlag(currACK_DW, true, bitNum);
            }

            // INVIO stato test...
            if (mTestingData.Value.ToString() != TestingData.Trim())
            {
                mTestingData.Value = TestingData.Trim();
            }

            // scrivo update ad ack SE VARIATO!!!
            if (currAck != (StFlag8)currACK_DW[0])
            {
                inizio = DateTime.Now;
                OsaiMemRW_Byte(W, OSAI.MemTypeWord.MW_CODE, memIndexAck, memOrderStart, ref currACK_DW);
                if (utils.CRB("recTime")) TimingData.addResult(string.Format("W{0}-ACK_DW0", currACK_DW.Length), DateTime.Now.Subtract(inizio).Ticks);
            }
        }
        /// <summary>
        /// Invia (se necessario) il watchdog di stato in vita... blink 0/1 ogni secondo
        /// </summary>
        private void sendWatchDog()
        {
            int memIndex = 0;
            bool setFlag = false;
            byte[] retACK_DW = new byte[1];
            // 2016.10.18 aggiungo scrittura del bit di "watchdog" al primo bit della DW2... come resto tra secondi / 2
            if (utils.CRB("sendWatchDog"))
            {
                int resto = 0;
                Math.DivRem(DateTime.Now.Second, 2, out resto);
                setFlag = resto == 0;
                // se watchdog è cambiato lo invio, altrimenti lascio come è...
                if (setFlag != lastWatchDog)
                {
                    lastWatchDog = setFlag;
                    retACK_DW = utils.setBitOnStFlag(retACK_DW, setFlag, 0); // imposto primo bit!!!
                    // scrivo su area PLC
                    memIndex = 19022;
                    inizio = DateTime.Now;
                    OsaiMemRW_Byte(W, OSAI.MemTypeWord.MW_CODE, memIndex, 0, ref retACK_DW);
                    if (utils.CRB("recTime")) TimingData.addResult(string.Format("W{0}-WatchDog", retACK_DW.Length), DateTime.Now.Subtract(inizio).Ticks);
                }
            }
        }
        /// <summary>
        /// Recupero info su test: numero + elenco parametri IN/OUT
        /// </summary>
        /// <param name="charSep"></param>
        /// <returns></returns>
        private string getTestData(string charSep)
        {
            string answ = "";
            // Area di memoria x test... (64 DWord) (la prima va splittata)
            UInt32[] MemBlockTestData = new UInt32[64];
            // variabili numero test e num parametri impiegati
            int testNum = 0;
            int numPar = 0;
            // variabile dove parcheggiare conversione codici dei parametri del test (IN o OUT)
            UInt32 testParam = 0;

            // leggo tutto!!!
            int memIndex = 19700;
            inizio = DateTime.Now;
            OsaiMemRW_DWord(R, OSAI.MemTypeWord.MW_CODE, memIndex, ref MemBlockTestData);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-TEST_DATA", MemBlockTestData.Length), DateTime.Now.Subtract(inizio).Ticks);

            // recupero cod univoco test e numero parametri impiegati
            testNum = BitConverter.ToUInt16(BitConverter.GetBytes(MemBlockTestData[0]), 0);
            numPar = BitConverter.ToUInt16(BitConverter.GetBytes(MemBlockTestData[0]), 2);


            // riporto separatore + numero test + num parametri
            answ = string.Format("{0}TN:{1}{0}NP:{2}", charSep, testNum, numPar);
            // se ci sono parametri da accodare li recupero!
            if (numPar > 0)
            {
                for (int i = 0; i < numPar; i++)
                {
                    testParam = MemBlockTestData[1 + i];
                    answ += string.Format("{0}{1}", charSep, testParam);
                }
            }

            return answ;
        }
        /// <summary>
        /// Gestione STROBE --> ACK per codici M/S/T
        /// </summary>
        /// <param name="currStrobe">byte di strobe corrente</param>
        /// <param name="bitNum">0/1/2</param>
        /// <param name="retACK_DW1">vettore da restituire di ACK</param>
        /// <param name="memShift">shift memoria x buffer dati da leggere</param>
        /// <param name="MemBlock">Vettore completo dei valori + buffer code M/S/T</param>
        /// <param name="Coda">Quale coda: M/S/T</param>
        private void gestStrobeCodMST(StFlag8 currStrobe, int bitNum, ref byte[] retACK_DW1, int memShift, byte[] MemBlock, string Coda)
        {
            int numEv = 0;
            int codEv = 0;
            if (currStrobe.HasFlag((StFlag8)Math.Pow(2, bitNum)))
            {
                // verifico sia > 0 il numero di valori da leggere - indice 0 sull'area...
                numEv = BitConverter.ToUInt16(MemBlock, 2 * memShift);
                if (numEv > 0)
                {
                    // il num ev rappresenta quanti slot 16bit (da 2 byte) sono stati valorizzati, VA FATTO CICLO E LETTI TUTTI
                    for (int i = 0; i < numEv; i++)
                    {
                        // leggo valore Codice
                        codEv = BitConverter.ToUInt16(MemBlock, 2 * (i + 2 + memShift));
                        // accodo evento Codice
                        appendCodeMST(Coda, codEv.ToString());
                    }
                }
                // memorizzo allarme nel vettore ack....
                retACK_DW1 = utils.setBitOnStFlag(retACK_DW1, true, bitNum);
            }
        }
        /// <summary>
        /// Ricarica dati da file di scambio con CMSConnect
        /// </summary>
        private void reloadDataFromFile()
        {
            if (utils.CRB("verbose")) lg.Info("Inizio lettura dati da file");
            int totRighe = 0;
            // da rivedere: se sono N+1 file (N=path + generale) deve essere ad esempio nomeComune_*.mtc --> ciclo per leggerne N
            string fileName = string.Format(@"{0}\{1}", utils.dataDir, utils.CRS("GeneralStatusFilePath"));
            string linea;
            // creo un vettore della dimensione corretta... conta anche commenti tanto poi riduco...
            generalStatus = new Dictionary<string, string>();
            try
            {
                totRighe = File.ReadLines(fileName).Count();
                // carica da file...
                System.IO.StreamReader file = new System.IO.StreamReader(fileName);
                if (file != null)
                {
                    // leggo 1 linea alla volta...
                    string[] valori;
                    while ((linea = file.ReadLine()) != null)
                    {
                        // SE non è un commento...
                        if (linea.Substring(0, 1) != "#")
                        {
                            valori = linea.Split(utils.CRC("testCharSep"));
                            generalStatus.Add(valori[0], valori[1]);
                        }
                    }
                    // chiudo file
                    file.Close();
                    if (utils.CRB("verbose")) lg.Info("Fine lettura dati da file");
                }
            }
            catch
            {
                lg.Info("Errore in lettura dati da file");
            }


            // ora salvo valori letti...
            foreach (KeyValuePair<string, string> item in generalStatus)
            {
                // verifico la chiave e nel caso aggiorno...
                switch (item.Key)
                {
                    case "OperatorId":
                        mOperator.Value = item.Value;
                        break;
                    case "Path_01_CurrProg":
                        istPathProgrName[0] = item.Value;
                        break;
                    case "Path_02_CurrProg":
                        istPathProgrName[1] = item.Value;
                        break;
                    case "Path_03_CurrProg":
                        istPathProgrName[2] = item.Value;
                        break;
                    case "Path_04_CurrProg":
                        istPathProgrName[3] = item.Value;
                        break;
                    case "Path_05_CurrProg":
                        istPathProgrName[4] = item.Value;
                        break;
                    case "Path_06_CurrProg":
                        istPathProgrName[5] = item.Value;
                        break;
                    case "Path_01_PartId":
                        istPathPartId[0] = item.Value;
                        break;
                    case "Path_02_PartId":
                        istPathPartId[1] = item.Value;
                        break;
                    case "Path_03_PartId":
                        istPathPartId[2] = item.Value;
                        break;
                    case "Path_04_PartId":
                        istPathPartId[3] = item.Value;
                        break;
                    case "Path_05_PartId":
                        istPathPartId[4] = item.Value;
                        break;
                    case "Path_06_PartId":
                        istPathPartId[5] = item.Value;
                        break;
                    default:
                        break;
                }
            }
        }
        /// <summary>
        /// Gestione strobe UserAction: salva dati e gestione ACK
        /// </summary>
        /// <param name="bitNum"></param>
        /// <param name="retACK_DW1"></param>
        /// <param name="UserAction"></param>
        /// <param name="azione"></param>
        private void gestStrobeUserAction(int bitNum, ref byte[] retACK_DW1, ref string UserAction, string azione)
        {
            if (STRB_DW1.HasFlag((StFlag32)Math.Pow(2, bitNum)))
            {
                // salvo evento UserAction in variabile...
                UserAction += azione;
                // memorizzo allarme nel vettore ack....
                retACK_DW1 = utils.setBitOnStFlag(retACK_DW1, true, bitNum);
            }
        }
        /// <summary>
        /// recupera allarmi ed aggiorna strobe (privato) degli allarmi
        /// </summary>
        /// <param name="Alarm2Refresh">32bit mask degli allarmi da aggiornare</param>
        /// <param name="giveAck">boolean: se si debba tornare ACK</param>
        public override void refreshAlarmState(StFlag32 Alarm2Refresh, bool giveAck)
        {
            // log bitmap se verboso attivo + ho allarmi da refreshare
            if ((Alarm2Refresh != StFlag32.NONE) && utils.CRB("verbose"))
            {
                lg.Info("Richiesto refresh allarmi x bitmask: {0}", utils.binaryForm((int)Alarm2Refresh));
            }

            bool ackReturned = false;
            // byte di acknowledge...
            byte[] retACK_DW0 = new byte[4];
            // inizio impostando una bitmap x ACK che abbia i bit abbassati se lo strobe è disattivo: AND logico tra STR e ACK
            retACK_DW0 = BitConverter.GetBytes(Convert.ToUInt32(STRB_DW0 & ACK_DW0));

            inizio = DateTime.Now;
            uint[] MemBlock;
            // primo blocco memoria allarmi
            int memIndex = 19036;
            // controllo, SE devo leggere tutto uso un unico accesso ai 128byte, altrimenti leggo a blocchi di 32bit...
            if (Alarm2Refresh == (StFlag32)unchecked((int)UInt32.MaxValue))
            {
                // blocco memoria x lettura dati COMPLETO (4Byte*32 = 128Byte)
                MemBlock = new uint[32];
                // recupero tutti i 32 bit del blocco
                inizio = DateTime.Now;
                OsaiMemRW_DWord(R, OSAI.MemTypeWord.MW_CODE, memIndex, ref MemBlock);
                if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-STRB_DW0", MemBlock.Length * 4), DateTime.Now.Subtract(inizio).Ticks);
                if (AlarmFlags != null)
                {
                    try
                    {
                        // aggiorno vettore allarmi x intero! size del vettore di destinazione (in byte!!!!)
                        Buffer.BlockCopy(MemBlock, 0, AlarmFlags, 0, AlarmFlags.Length);
                    }
                    catch (Exception exc)
                    {
                        lg.Error(string.Format("Errore in BLOCKCOPY{0}{1}", Environment.NewLine, exc));
                    }
                }
            }
            else
            {
                if (Alarm2Refresh != StFlag32.NONE)
                {
                    // blocco memoria x lettura dati
                    MemBlock = new uint[1];
                    // verifico gli allarmi di tutti i bit alzati...
                    for (int i = 0; i < 32; i++)
                    {
                        if (Alarm2Refresh.HasFlag((StFlag32)Math.Pow(2, i)))
                        {
                            // recupero tutti i 32 bit del blocco
                            inizio = DateTime.Now;
                            OsaiMemRW_DWord(R, OSAI.MemTypeWord.MW_CODE, memIndex + i * 2, ref MemBlock);
                            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-STRB_DW0", MemBlock.Length * 4), DateTime.Now.Subtract(inizio).Ticks);
                            try
                            {
                                // aggiorno vettore allarmi x intero!
                                Buffer.BlockCopy(MemBlock, 0, AlarmFlags, i * 4, 4);
                            }
                            catch (Exception exc)
                            {
                                lg.Error(string.Format("Errore in BLOCKCOPY per indice i = {2},{0}{1}", Environment.NewLine, exc, i));
                            }
                            if (utils.CRI("loglevel") > 5)
                            {
                                lg.Info("Bit {0}, MemBlock: {1}", i, MemBlock[0]);
                            }
                            // segnalo allarme letto! memorizzo allarme nel vettore ack....
                            retACK_DW0 = utils.setBitOnStFlag(retACK_DW0, true, i);
                        }
                    }
                    // scrivo ack se richiesto!!!
                    if (giveAck)
                    {
                        // mi preparo a scrivere ACK
                        memIndex = 19018;
                        // scrivo update ad ack!!!
                        inizio = DateTime.Now;
                        OsaiMemRW_Byte(W, OSAI.MemTypeWord.MW_CODE, memIndex, 0, ref retACK_DW0);
                        if (utils.CRB("recTime")) TimingData.addResult(string.Format("W{0}-ACK_DW0", retACK_DW0.Length), DateTime.Now.Subtract(inizio).Ticks);
                        ackReturned = true;
                    }
                }
            }

            // SE non ho già tornato un ACK...
            if (!ackReturned)
            {
                // se è diverso il vettori degli ACK in memoria da quello PLC...
                if (ACK_DW0 != (StFlag32)BitConverter.ToUInt32(retACK_DW0, 0))
                {
                    memIndex = 19018;
                    // invio INTERO set ACK_DW0 con bit abbassati...
                    retACK_DW0 = BitConverter.GetBytes(Convert.ToUInt32(STRB_DW0 & ACK_DW0));
                    inizio = DateTime.Now;
                    OsaiMemRW_Byte(W, OSAI.MemTypeWord.MW_CODE, memIndex, 0, ref retACK_DW0);
                    if (utils.CRB("recTime")) TimingData.addResult(string.Format("W{0}-ACK_DW0", retACK_DW0.Length), DateTime.Now.Subtract(inizio).Ticks);
                }
            }
        }
        /// <summary>
        /// processing!
        /// </summary>
        public override void processStatus()
        {
            // update status da DW2/ DW3

            // EMstop: verifico BIT e di conseguenza imposto
            if (STRB_DW2.HasFlag(StFlag32.B00))
            {
                mEStop.Value = "TRIGGERED";
            }
            else
            {
                mEStop.Value = "ARMED";
            }

            // verifico POWER ON...
            if (STRB_DW2.HasFlag(StFlag32.B01))
            {
                mPower.Value = "ON";
            }
            else
            {
                mPower.Value = "OFF";
            }

            // Processo selezionato 0=P1, 1=P2
            string procSel = "";
            if (STRB_DW2.HasFlag(StFlag32.B02))
            {
                procSel = "P2";
            }
            else
            {
                procSel = "P1";
            }
            if (mProcSel.Value.ToString() != procSel)
            {
                mProcSel.Value = procSel;
            }

            // processo ora i dai dei path... di sicuro il primo
            procPath01_Status();
            //...e se c'è pure il secondo...
            if (currAdpConf.nPath > 1)
            {
                // anche il secondo!
                procPath02_Status();
            }
        }
        /// <summary>
        /// Processa status del path 01...
        /// </summary>
        private void procPath01_Status()
        {
            // HARD CODE: forzo path 1 (indice 0...) x iniziare...
            int idxPath = 0;
            string pathType = "";
            // verifico tipo processo path...
            if (STRB_DW2.HasFlag(StFlag32.B03))
            {
                pathType = "LAVORO";
            }
            else
            {
                pathType = "ASSERV";
            }

            // switch su RUN mode...
            string runMode = "";
            if (STRB_DW2.HasFlag(StFlag32.B04))
            {
                runMode = "AUTOMATIC";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B05))
            {
                runMode = "EDIT";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B06))
            {
                runMode = "MANUAL_DATA_INPUT";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B07))
            {
                runMode = "MANUAL";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B08))
            {
                runMode = "SEMI_AUTO";
            }

            // switch su EXE mode...
            string exeMode = "";
            if (STRB_DW2.HasFlag(StFlag32.B09))
            {
                exeMode = "ACTIVE";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B10))
            {
                exeMode = "READY";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B11))
            {
                exeMode = "STOPPED";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B12))
            {
                exeMode = "FEED_HOLD";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B13))
            {
                exeMode = "OPTIONAL_STOP";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B14))
            {
                exeMode = "PROGRAM_STOPPED";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B15))
            {
                exeMode = "PROGRAM_COMPLETED";
            }

            // inizializzo SEMPRE a vuoto...
            string SubMode = "";
            // verifico sulla STRB_DW3 i submode che POTREBBERO tutti sovrapposti...
            if (STRB_DW3 != StFlag32.NONE)
            {
                // cerco i bit alzati --> aggiungo relativo submode!
                for (int i = 0; i < 32; i++)
                {
                    // converto! e aggiungo allarmi sollevati al corretto controller allarmi...
                    if (STRB_DW3.HasFlag((StFlag32)Math.Pow(2, i)))
                    {
                        SubMode += string.Format(" [P{0:00}_{1}]", idxPath, elencoSubMode[i.ToString()]);
                    }
                }
            }

            // salvo in blocco le info ricostruite x path!
            savePathData(idxPath, pathType, runMode, exeMode, SubMode.Trim());
        }
        /// <summary>
        /// Processa status del path 02...
        /// </summary>
        private void procPath02_Status()
        {
            // HARD CODE: forzo path 2 (indice 1)...
            int idxPath = 1;
            string pathType = "";
            // verifico tipo processo path...
            if (STRB_DW2.HasFlag(StFlag32.B16))
            {
                pathType = "LAVORO";
            }
            else
            {
                pathType = "ASSERV";
            }

            // switch su RUN mode...
            string runMode = "";
            if (STRB_DW2.HasFlag(StFlag32.B17))
            {
                runMode = "AUTOMATIC";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B18))
            {
                runMode = "EDIT";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B19))
            {
                runMode = "MANUAL_DATA_INPUT";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B20))
            {
                runMode = "MANUAL";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B21))
            {
                runMode = "SEMI_AUTO";
            }

            // switch su EXE mode...
            string exeMode = "";
            if (STRB_DW2.HasFlag(StFlag32.B22))
            {
                exeMode = "ACTIVE";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B23))
            {
                exeMode = "READY";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B24))
            {
                exeMode = "STOPPED";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B25))
            {
                exeMode = "FEED_HOLD";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B26))
            {
                exeMode = "OPTIONAL_STOP";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B27))
            {
                exeMode = "PROGRAM_STOPPED";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B28))
            {
                exeMode = "PROGRAM_COMPLETED";
            }

            // inizializzo SEMPRE a vuoto...
            string SubMode = "";
            // verifico sulla STRB_DW3 i submode che POTREBBERO tutti sovrapposti...
            if (STRB_DW4 != StFlag32.NONE)
            {
                // cerco i bit alzati --> aggiungo relativo submode!
                for (int i = 0; i < 32; i++)
                {
                    // converto! e aggiungo allarmi sollevati al corretto controller allarmi...
                    if (STRB_DW4.HasFlag((StFlag32)Math.Pow(2, i)))
                    {
                        SubMode += string.Format(" [P{0:00}_{1}]", idxPath, elencoSubMode[i.ToString()]);
                    }
                }
            }

            // salvo in blocco le info ricostruite x path!
            savePathData(idxPath, pathType, runMode, exeMode, SubMode.Trim());
        }
        /// <summary>
        /// Salva i valori per il path indicato SE variati...
        /// </summary>
        /// <param name="idxPath"></param>
        /// <param name="pathType"></param>
        /// <param name="pathRunMode"></param>
        /// <param name="pathExeMode"></param>
        /// <param name="pathSubMode"></param>
        protected void savePathData(int idxPath, string pathType, string pathRunMode, string pathExeMode, string pathSubMode)
        {
            // salvo type
            if (vettPath[idxPath].mPathType.Value.ToString() != pathType)
            {
                vettPath[idxPath].mPathType.Value = pathType;
            }
            // salvo RUN mode
            if (vettPath[idxPath].mPathRunMode.Value.ToString() != pathRunMode)
            {
                vettPath[idxPath].mPathRunMode.Value = pathRunMode;
            }
            // salvo EXE mode
            if (vettPath[idxPath].mPathExeMode.Value.ToString() != pathExeMode)
            {
                vettPath[idxPath].mPathExeMode.Value = pathExeMode;
            }
            // salvo i submode..
            if (vettPath[idxPath].mPathSubMode.Value.ToString() != pathSubMode)
            {
                vettPath[idxPath].mPathSubMode.Value = pathSubMode;
            }
        }
        /// <summary>
        /// Recupera dati a lenta variazione
        /// </summary>
        public override void getSlowChangingData()
        {
            base.getSlowChangingData();

            // reload dati da file...
            reloadDataFromFile();

            // area gestione dati utensili... in base al num max di UnOp recupero i dati utensile... PROCEDURA AD HOC!!!
            getDatiUt(currAdpConf.nUnOp);
        }
        /// <summary>
        /// Gestione lettura dati manutenzione
        /// </summary>
        private void getMtzDataFromPlc()
        {
            // recupero i dati di manutenzione dall'area di memoria IN BLOCCO
            int memIndex = 1000;
            uint[] tabDatiMtz = new uint[maintData.Length];
            inizio = DateTime.Now;
            OsaiMemRW_DWord(R, OSAI.MemTypeWord.GW_CODE, memIndex, ref tabDatiMtz);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-DatiMtz", tabDatiMtz.Length * 4), DateTime.Now.Subtract(inizio).Ticks);
            // decodifico aree memoria secondo tab configurazione
            processMaintData(tabDatiMtz, maintData);
            // verifica se debba salvare e mostrare dati
            checkSavePersDataLayer();
        }

        /// <summary>
        /// Gestione lettura dati status da PLC
        /// </summary>
        private void getStatusDataFromPlc()
        {
            // recupero i dati di manutenzione dall'area di memoria IN BLOCCO
            int memIndex = 19426;
            int numByte = 1 + (statusData.Length / 8);
            byte[] tabDatiStatus = new byte[numByte];
            inizio = DateTime.Now;
            OsaiMemRW_Byte(R, OSAI.MemTypeWord.MW_CODE, memIndex, 0, ref tabDatiStatus);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-DatiStatus", tabDatiStatus.Length), DateTime.Now.Subtract(inizio).Ticks);

            // decodifico aree memoria secondo tab configurazione
            processStatusData(tabDatiStatus, statusData);
        }
        /// <summary>
        /// Leggo dati globali comuni (x path, assi...)
        /// </summary>
        public override void getGlobalData()
        {
            base.getGlobalData();
            // svuoto...
            parentForm.dataMonitor_1 = "";
            parentForm.dataMonitor_2 = "";
            parentForm.dataMonitor_3 = "";

            // accodo dati path in DataMonitor......
            StringBuilder sb1 = new StringBuilder();
            StringBuilder sb2 = new StringBuilder();

            // dati base...
            sb1.AppendLine(string.Format("POWER {0}: | EmStop:{1:N3} | ProcSel: {2}", mPower.Value, mEStop.Value, mProcSel.Value));

            // leggo dati globali...
            inizio = DateTime.Now;
            OSAI_ref.O_GetNcInfo1(ref allNcData);
            if (utils.CRB("recTime")) TimingData.addResult("R-NcInfo", DateTime.Now.Subtract(inizio).Ticks);

            // recupero speed e feed...
            FeedRate = Convert.ToInt32(allNcData.realfeed);
            SpeedRate = Convert.ToInt32(allNcData.realspeed);

            // area path1/2: 7 WORD x ogni path...
            int memIndex = 19151;
            ushort[] PathData_mem = new ushort[14];
            inizio = DateTime.Now;
            OsaiMemRW_Word(R, OSAI.MemTypeWord.MW_CODE, memIndex, ref PathData_mem);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-PathData", PathData_mem.Length * 2), DateTime.Now.Subtract(inizio).Ticks);

            // inizio indicando feed...
            sb1.AppendLine(string.Format("FeedRate: {0} mm/min", FeedRate));
            sb1.AppendLine(string.Format("SpeedRate: {0} mm/min", SpeedRate));

            // 2017.04.20: recupero OVER per Jog/Feed/Rapid/Spindle x i path multipli (qui cablati 1-2)
            ushort[] currPathData = new ushort[7];
            // processo ora i dai dei path... di sicuro il primo
            Array.Copy(PathData_mem, 0, currPathData, 0, 7);
            procPathOverride(0, currPathData, ref sb1);
            //...e se c'è pure il secondo...
            if (currAdpConf.nPath > 1)
            {
                Array.Copy(PathData_mem, 7, currPathData, 0, 7);
                // anche il secondo!
                procPathOverride(1, currPathData, ref sb1);
            }

            // se ho allarmi li accodo...
            if (allNcData.lastncerror != 0)
            {
                sb1.AppendLine(string.Format("Allarmi CNC: {0}", allNcData.lastncerror));

                inizio = DateTime.Now;
                OSAI_ref.O_ReadCurrentErrorMsg(ref allarmiCNC);
                if (utils.CRB("recTime")) TimingData.addResult("R-CNC-ERROR-MSG", DateTime.Now.Subtract(inizio).Ticks);
                checkCNCAlarms();

#if false
                // provo altri metodi...
                CMSCncLib.OPENcontrol.MSGEMERGENCY emergCNC = new CMSCncLib.OPENcontrol.MSGEMERGENCY();
                OSAI_ref.O_ReadCurrentEmergMsg(ref emergCNC);
                CMSCncLib.OPENcontrol.MSGANOMALY anomCNC = new CMSCncLib.OPENcontrol.MSGANOMALY();
                OSAI_ref.O_ReadCurrentAnomalyMsg(ref anomCNC); 
#endif
            }
            else
            {
                allarmiCNC = new CMSCncLib.OPENcontrol.MSGERROR();
            }

            // accodo dati base!
            parentForm.dataMonitor_1 = sb1.ToString();
            // accodo dati UT...
            for (int i = 0; i < currAdpConf.nUnOp; i++)
            {
                sb2.AppendLine(string.Format("UT: ToolId {0} | VitaRes {1} | TipoVita {2} ", vettUnOp[i].mUnOpToolId.Value, vettUnOp[i].mUnOpVitaRes.Value, vettUnOp[i].mUnOpVitaResType.Value));
            }
            parentForm.dataMonitor_2 = sb2.ToString();

            // gestisco lettura dati manutenzione da PLC...
            getMtzDataFromPlc();

            // gestisco lettura dati status da PLC...
            getStatusDataFromPlc();
        }
        /// <summary>
        /// Processa i dati di override di un path
        /// </summary>
        /// <param name="currIdx">idx path (zero based)</param>
        /// <param name="dataVector">vettore 7 valori dei dati (valori int16 jog/feed/rapid/spindle[1..4])</param>
        /// <param name="sb">StringBuilder da aggiornare</param>
        private void procPathOverride(int currIdx, ushort[] dataVector, ref StringBuilder sb)
        {
            // prendo quella GLOBALE...
            vettPath[currIdx].mPathFeed.Value = FeedRate;
            // leggo da vettore... CABLATO DIVISO PER 100!!!
            vettPath[currIdx].mPathJogOver.Value = dataVector[0] / 100;
            vettPath[currIdx].mPathFeedOver.Value = dataVector[1] / 100;
            vettPath[currIdx].mPathRapidOver.Value = dataVector[2] / 100;
            vettPath[currIdx].mPathSpindleOver_01.Value = dataVector[3] / 100;
            vettPath[currIdx].mPathSpindleOver_02.Value = dataVector[4] / 100;
            vettPath[currIdx].mPathSpindleOver_03.Value = dataVector[5] / 100;
            vettPath[currIdx].mPathSpindleOver_04.Value = dataVector[6] / 100;

            // salvo stringa!
            sb.AppendLine(string.Format("P{0} - Override % Jog | Feed | Rapid: {1} | {2} | {3}", currIdx + 1, dataVector[0] / 100, dataVector[1] / 100, dataVector[2] / 100));
            sb.AppendLine(string.Format("P{0} - Override % Spindle 1 | 2 | 3 | 4: {1} | {2} | {3} | {4}", currIdx + 1, dataVector[3] / 100, dataVector[4] / 100, dataVector[5] / 100, dataVector[6] / 100));
        }

        /// <summary>
        /// Carico file conf dati CMS
        /// </summary>
        protected override void loadOtherFile()
        {
            base.loadOtherFile();
            loadMaintData();
            loadStatusData();
        }
        /// <summary>
        /// Lettura file gestione dati manutenzione da PLC
        /// </summary>
        private void loadMaintData()
        {
            // carico dati x Maintenance
            if (utils.CRB("verbose")) lg.Info("Inizio caricamento vettore variabili manutenzione gestite");
            int totRighe = 0;
            string fileName = string.Format(@"{0}\{1}", utils.confDir, utils.CRS("CounterListFilePath"));
            string linea;
            totRighe = File.ReadLines(fileName).Count();
            // creo un vettore della dimensione corretta... conta anche commenti tanto poi riduco...
            maintData = new otherData[File.ReadLines(fileName).Count()];
            // carica da file...
            System.IO.StreamReader file = new System.IO.StreamReader(fileName);
            // leggo 1 linea alla volta...
            int numRiga = 0;
            while ((linea = file.ReadLine()) != null)
            {
                // SE non è un commento...
                if (linea.Substring(0, 1) != "#")
                {
                    //elencoAllarmi[rumRiga] = decodeAlarmLine(linea, ':');
                    maintData[numRiga] = decodeOtherData(linea, utils.CRC("testCharSep"), "MW", 19166, 2);
                    numRiga++;
                }
            }
            // chiudo file
            file.Close();
            // ora trimmo vettore al solo numero VERO dei valori caricati...
            Array.Resize<otherData>(ref maintData, numRiga);

            if (utils.CRB("verbose")) lg.Info(string.Format("Fine caricamento vettore di {0} variabili manutenzione gestite", numRiga));
        }
        /// <summary>
        /// Lettura file gestione dati status da PLC
        /// </summary>
        private void loadStatusData()
        {
            // carico dati x Maintenance
            if (utils.CRB("verbose")) lg.Info("Inizio caricamento vettore variabili status gestite");
            int totRighe = 0;
            string fileName = string.Format(@"{0}\{1}", utils.confDir, utils.CRS("StatusListFilePath"));
            string linea;
            totRighe = File.ReadLines(fileName).Count();
            // creo un vettore della dimensione corretta... conta anche commenti tanto poi riduco...
            statusData = new otherData[File.ReadLines(fileName).Count()];
            // carica da file...
            StreamReader file = new StreamReader(fileName);
            // leggo 1 linea alla volta...
            int numRiga = 0;
            while ((linea = file.ReadLine()) != null)
            {
                // SE non è un commento...
                if (linea.Substring(0, 1) != "#")
                {
                    statusData[numRiga] = decodeBitData(linea, utils.CRC("testCharSep"), "ML", 19426, 2, numRiga);
                    numRiga++;
                }
            }
            // chiudo file
            file.Close();
            // ora trimmo vettore al solo numero VERO dei valori caricati...
            Array.Resize<otherData>(ref statusData, numRiga);

            if (utils.CRB("verbose")) lg.Info(string.Format("Fine caricamento vettore di {0} variabili status gestite", numRiga));
        }
        /// <summary>
        /// Processing allarmi CNC
        /// </summary>
        public override void processAlarm()
        {
            base.processAlarm();

            // aggiungo gestione allarmi CNC
            checkCNCAlarms();
        }
        /// <summary>
        /// Verifica i 10 allarmi CNC SE presenti
        /// </summary>
        private void checkCNCAlarms()
        {
            if (allarmiCNC != null)
            {
                sendAlarmIfPresent(allarmiCNC);
            }
        }
        /// <summary>
        /// Invia singolo allarme CNC se presente
        /// </summary>
        /// <param name="allarme"></param>
        protected void sendAlarmIfPresent(CMSCncLib.OPENcontrol.MSGERROR allarme)
        {
            // controllo valore...
            if (allarme != null)
            {
                try
                {
                    StringBuilder sb = new StringBuilder();
                    if (utils.CRI("loglevel") > 5)
                    {
                        lg.Info(string.Format("Allarmi CNC: {0} ", allarme.CodeErr));
                    }
                    string alarmText = string.Format("Cod: {0} | Proc: {1} | Comm: {2} | SubCom: {3} | {4}", allarme.CodeErr, allarme.Process, allarme.Comando, allarme.SubCom, allarme.FormatTxt);
                    mAlarmCNC.Add(MTConnect.Condition.Level.FAULT, alarmText, allarme.CodeErr.ToString(), "", "");
                    // mostro in form!
                    sb.AppendLine(alarmText);
                    parentForm.dataMonitor_1 += sb.ToString();
                }
                catch (Exception exc)
                {
                    lg.Error(string.Format("{0}", exc));
                }
            }
        }
        /// <summary>
        /// Recupero dati x UnOp
        /// </summary>
        public override void getUnOp()
        {
            base.getUnOp();
            // !!!FARE!!! NON HO trovato nulla, verificare con Andrea...
#if false

            // leggo in blocco tutte le speed da memoria...
            int memIndex = 3140;
            // buffer memoria 60 byte... speed (16bit 2 * 20) + load (8bit 1 * 20) visto che sono 20 teste max
            byte[] unOpSpeedMem = new byte[60];

            inizio = DateTime.Now;
            OsaiMemRW(R, FANUC.MemType.D, memIndex, ref unOpSpeedMem);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-SPEED_UNOP", unOpSpeedMem.Length), DateTime.Now.Subtract(inizio).Ticks);

            StringBuilder sb = new StringBuilder();

            int numUnOp = currAdpConf.nUnOp;
            // cicl su path x leggere le velocità dell'UnOp
            for (int i = 0; i < numUnOp; i++)
            {
                // leggo valore Codice
                SpeedRate = BitConverter.ToUInt16(unOpSpeedMem, 2 * i);

                // leggo 41° byte = indice 40 + 2*i x le var del carico UnOp
                UnOpLoad = unOpSpeedMem[40 + i];

                //vettUnOp[i].mUnOpNumCU.Value = uoData.UnOpNumCU;
                //vettUnOp[i].mUnOpStatus.Value = uoData.UnOpStatus;
                //vettUnOp[i].mUnOpAccTime.Value = uoData.UnOpAccTime;

                sb.AppendLine(string.Format("SpeedRate UnOp_{0}: {1} rpm", i + 1, SpeedRate));
                sb.AppendLine(string.Format("Load UnOp_{0}: {1}", i + 1, UnOpLoad));
            }


            parentForm.dataMonitor += sb.ToString(); 
#endif
        }

        /// <summary>
        /// Procedura di processing lettura memoria x DatiUt - Step 4
        /// </summary>
        /// <param name="int32Mem"></param>
        /// <param name="j"></param>
        /// <returns></returns>
        public override int getDatiUt_step4(ref byte[] int32Mem, int j)
        {
            int memIndex = 6200 + 2 * j;
            inizio = DateTime.Now;
            OsaiMemRW_Byte(R, OSAI.MemTypeWord.MW_CODE, memIndex, 0, ref int32Mem);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-TabUT-VitaRes", int32Mem.Length), DateTime.Now.Subtract(inizio).Ticks);
            return memIndex;
        }
        /// <summary>
        /// Procedura di processing lettura memoria x DatiUt - Step 3b
        /// </summary>
        /// <param name="int8Mem"></param>
        /// <param name="j"></param>
        /// <returns></returns>
        public override int getDatiUt_step3b(ref byte int8Mem, int j)
        {
            int memIndex = 5000 + 1 * j / 2; // dovrebeb leggere correttamente solo 1 byte...
            byte[] byteMtrx = new byte[1];
            int resto = 0;
            Math.DivRem(j, 2, out resto);
            inizio = DateTime.Now;
            OsaiMemRW_Byte(R, OSAI.MemTypeWord.MW_CODE, memIndex, resto, ref byteMtrx);
            int8Mem = byteMtrx[0];
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-TabUT-FamUT", 8), DateTime.Now.Subtract(inizio).Ticks);
            return memIndex;
        }
        /// <summary>
        /// Procedura di processing lettura memoria x DatiUt - Step 3a
        /// </summary>
        /// <param name="tabFam_FamUt"></param>
        /// <returns></returns>
        public override int getDatiUt_step3a(ref byte[] tabFam_FamUt)
        {
            int memIndex = 4100;
            inizio = DateTime.Now;
            OsaiMemRW_Byte(R, OSAI.MemTypeWord.MW_CODE, memIndex, 0, ref tabFam_FamUt);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-TabFamUT-FamUT", tabFam_FamUt.Length), DateTime.Now.Subtract(inizio).Ticks);
            return memIndex;
        }
        /// <summary>
        /// Procedura di processing lettura memoria x DatiUt - Step 2b
        /// </summary>
        /// <param name="int16Mem"></param>
        /// <param name="j"></param>
        /// <returns></returns>
        public override int getDatiUt_step2b(ref byte[] int16Mem, int j)
        {
            int memIndex = 5500 + j;
            inizio = DateTime.Now;
            OsaiMemRW_Byte(R, OSAI.MemTypeWord.MW_CODE, memIndex, 0, ref int16Mem);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-TabUT-FamUT", int16Mem.Length), DateTime.Now.Subtract(inizio).Ticks);
            return memIndex;
        }
        /// <summary>
        /// Procedura di processing lettura memoria x DatiUt - Step 2a
        /// </summary>
        /// <param name="tabUt_UT"></param>
        /// <returns></returns>
        public override int getDatiUt_step2a(ref byte[] tabUt_UT)
        {
            int memIndex = 5300;
            inizio = DateTime.Now;
            OsaiMemRW_Byte(R, OSAI.MemTypeWord.MW_CODE, memIndex, 0, ref tabUt_UT);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-TabUT-UT", tabUt_UT.Length), DateTime.Now.Subtract(inizio).Ticks);
            return memIndex;
        }
        /// <summary>
        /// Procedura di processing lettura memoria x DatiUt - Step 1
        /// </summary>
        /// <param name="elencoUtMem"></param>
        /// <returns></returns>
        public override int getDatiUt_step1(ref byte[] elencoUtMem)
        {
            int memIndex = 3020;
            inizio = DateTime.Now;
            OsaiMemRW_Byte(R, OSAI.MemTypeWord.MW_CODE, memIndex, 0, ref elencoUtMem);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-NUMUT", elencoUtMem.Length), DateTime.Now.Subtract(inizio).Ticks);
            return memIndex;
        }

        public override void getPath()
        {
            // recupero vettore generale G MODE
            inizio = DateTime.Now;
            ushort[] vettGCodes = new ushort[14];
            OSAI_ref.getPathGCodeMod(ref vettGCodes);
            if (utils.CRB("recTime")) TimingData.addResult("R-GCodModal", DateTime.Now.Subtract(inizio).Ticks);

            // attenzione: il vettore contiene come campi "vuoti" il valore MAX di USHort (16bit) ovvero 2^16 -1 (base 0) --> 65535
            string GCodAttivi = "";
            for (int i = 0; i < vettGCodes.Length; i++)
            {
                // se il valore è < di UShort.max (65535)...
                if (vettGCodes[i] < ushort.MaxValue)
                {
                    GCodAttivi += string.Format("[G{0}]", vettGCodes[i]);
                }
            }

            // NON ci sono nella documentazione GCode OneShot, verificare con Andrea...
#if false
            inizio = DateTime.Now;
            vettGCodes = OSAI_ref.getPathGCode1Shot();
            if (utils.CRB("recTime")) TimingData.addResult("R-GCodOneShot", DateTime.Now.Subtract(inizio).Ticks);
            if (vettGCodes.gcd0.flag > 0) GCodAttivi += string.Format("[{0}]", vettGCodes.gcd0.code);
            if (vettGCodes.gcd1.flag > 0) GCodAttivi += string.Format("[{0}]", vettGCodes.gcd1.code);
            if (vettGCodes.gcd2.flag > 0) GCodAttivi += string.Format("[{0}]", vettGCodes.gcd2.code);
            if (vettGCodes.gcd3.flag > 0) GCodAttivi += string.Format("[{0}]", vettGCodes.gcd3.code);

#endif

            // NON ci sono nella documentazione metodi x recuperare posizione PuntaUtensile, verificare con Andrea...
#if false

            inizio = DateTime.Now;
            CMSCncLib.Focas1.ODB5AXMAN vettTTCoord = OSAI_ref.getPathTTCoord();
            if (utils.CRB("recTime")) TimingData.addResult("R-TTCoord", DateTime.Now.Subtract(inizio).Ticks);
#endif

            // ciclo su path
            StringBuilder sb_2;
            StringBuilder sb_3;
            for (int i = 0; i < currAdpConf.nPath; i++)
            {
                // accodo dati path in DataMonitor...
                sb_2 = new StringBuilder();
                sb_3 = new StringBuilder();

                vettPath[i].mPathCodG_Act.Value = GCodAttivi;

#if false
                vettPath[i].mPathPosActX.Value = ((decimal)vettTTCoord.data1) / utils.CRI("fattdecimale");
                vettPath[i].mPathPosActY.Value = ((decimal)vettTTCoord.data2) / utils.CRI("fattdecimale");
                vettPath[i].mPathPosActZ.Value = ((decimal)vettTTCoord.data3) / utils.CRI("fattdecimale");
                vettPath[i].mPathPosActI.Value = ((decimal)vettTTCoord.c1) / utils.CRI("fattdecimale");
                vettPath[i].mPathPosActJ.Value = ((decimal)vettTTCoord.c2) / utils.CRI("fattdecimale");
                vettPath[i].mPathPosActK.Value = "0"; // è 5 assi...nullo...  
#endif

                // 2017.01.16 da rivedere
                vettPath[i].mPathCurrProgRowNum.Value = "";
                vettPath[i].mPathActiveAxes.Value = "";

                sb_2.AppendLine(string.Format("Path {0}, PROG: {1}", i + 1, vettPath[i].mPathCurrProg.Value));
                sb_3.AppendLine(string.Format("Path {0}, GCodes: {1}", i + 1, GCodAttivi));

#if false
                sb.AppendLine(string.Format("Path {0}, Asse 1: {1:N3}", i + 1, ((decimal)vettTTCoord.data1) / utils.CRI("fattdecimale")));
                sb.AppendLine(string.Format("Path {0}, Asse 2: {1:N3}", i + 1, ((decimal)vettTTCoord.data2) / utils.CRI("fattdecimale")));
                sb.AppendLine(string.Format("Path {0}, Asse 3: {1:N3}", i + 1, ((decimal)vettTTCoord.data3) / utils.CRI("fattdecimale")));
                sb.AppendLine(string.Format("Path {0}, Cent 1: {1:N3}", i + 1, ((decimal)vettTTCoord.c1) / utils.CRI("fattdecimale")));
                sb.AppendLine(string.Format("Path {0}, Cent 2: {1:N3}", i + 1, ((decimal)vettTTCoord.c2) / utils.CRI("fattdecimale"))); 
#endif

                parentForm.dataMonitor_2 += sb_2.ToString();
                parentForm.dataMonitor_3 += sb_3.ToString();
            }
        }

        public override void getAxis()
        {
            // mostro assi in DataMonitor......
            StringBuilder sb = new StringBuilder();

            inizio = DateTime.Now;
            // inizializzo x numero assi...
            CMSCncLib.OPENcontrol.GETINTDATA[] posAssiTgt = new CMSCncLib.OPENcontrol.GETINTDATA[currAdpConf.nAxis];
            CMSCncLib.OPENcontrol.GETINTDATA[] posAssi = new CMSCncLib.OPENcontrol.GETINTDATA[currAdpConf.nAxis];
            OSAI_ref.getAllAxisPos(1, ref posAssiTgt);
            OSAI_ref.getAllAxisPos(2, ref posAssi);
            if (utils.CRB("recTime")) TimingData.addResult("R-AXIS_POS", DateTime.Now.Subtract(inizio).Ticks);

            // NON c'è LOAD assi... verificare con Andrea
#if false
            inizio = DateTime.Now;
            CMSCncLib.Focas1.ODBSVLOAD loadAssi = OSAI_ref.getAllAxisLoad();
            if (utils.CRB("recTime")) TimingData.addResult("R-AXIS_LOAD", DateTime.Now.Subtract(inizio).Ticks); 
#endif

            // nuova posizione (per calcoli)
            double newPos = 0;
            double newPosTgt = 0;
            double distPerc = 0;
            int newDir = 0;
            string tipoAsse = "";
            string direzione = "";
            char axName;
            // ciclo x assi
            for (int i = 0; i < currAdpConf.nAxis; i++)
            {
                // per sicurezza try-catch
                try
                {
                    //string axName = System.Text.Encoding.ASCII.GetString(posAssi[i].AxisName);
                    axName = Convert.ToChar(posAssiTgt[i].AxisName);
                    newPos = posAssi[i].position;
                    newPosTgt = posAssiTgt[i].position;
                    // popolo valori... tranne LOAD
#if false
                    vettAxis[i].mAxLoad.Value = (double)(valLoad.data) / Math.Pow(10, valLoad.dec); 
                    newPos = (double)(valPos.abs.data) / Math.Pow(10, valPos.abs.dec);
#endif

                    vettAxis[i].mAxPosAct.Value = newPos;
                    vettAxis[i].mAxPosTgt.Value = newPosTgt;

                    vettAxis[i].mAxFeedAct.Value = FeedRate;

                    // calcolo distanza e salvo valore...
                    distPerc = newPos - prevPosAxis[i];

                    // sistemo direzione +/- (POS/NEG se lineari, CCW/CW se rotativi)
                    if (distPerc != 0)
                    {
                        newDir = Convert.ToInt32(distPerc / Math.Abs(distPerc));
                    }
                    else
                    {
                        newDir = prevDirAxis[i];
                    }

                    // verifico tipo direzione da tipo asse...
                    tipoAsse = vettAxis[i].mAxType.Value.ToString();
                    if (tipoAsse == "LINEAR")
                    {
                        // ?: conditional operator.  
                        direzione = (newDir > 0) ? "POSITIVE" : "NEGATIVE";
                    }
                    else if (tipoAsse == "ROTARY")
                    {
                        direzione = (newDir > 0) ? "CLOCKWISE" : "COUNTER_CLOCKWISE";
                    }

                    vettAxis[i].mAxDir.Value = direzione;


                    if (utils.CRB("verbose"))
                    {
                        sb.AppendLine(string.Format("Asse {0}: PosAct:{1:N3}, Prg:{2:N3} | {3}", axName, newPos, newPosTgt, direzione));
                    }

                    // salvo valori vettore prec...
                    prevPosAxis[i] = newPos;
                    prevDirAxis[i] = newDir;

                    //vettAxis[i].mAxMainProc.Value = AxData.AxisMainProc;
                    //vettAxis[i].mAxIsMaster.Value = AxData.AxisIsMaster;
                    //vettAxis[i].mAxMastId.Value = AxData.AxisMastId;
                    vettAxis[i].mAxFeedOver.Value = FeedRateOver;
                    //vettAxis[i].mAxAccelAct.Value = AxData.AxisAccel;
                    //vettAxis[i].mAxBattery.Value = AxData.AxisBattery;
                }
                catch
                {
                    lg.Error(string.Format("Errore in lettura asse {0}", i));
                }
            }

            parentForm.dataMonitor_3 += sb.ToString();
        }
    }
}
