using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CMSCncLib.CNC;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.IO;

namespace MTC_Adapter
{
    public class AdapterFanuc : AdapterGeneric
    {
        // vettore valori in file interscambio
        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 FANUC
        /// </summary>
        protected FANUC FANUC_ref;

        /// <summary>
        /// wrapper chiamata lettura/scrittura fanuc...
        /// </summary>
        /// <param name="bWrite"></param>
        /// <param name="MemType"></param>
        /// <param name="memIndex"></param>
        /// <param name="Value"></param>
        /// <returns></returns>
        public bool FanucMemRW(bool bWrite, FANUC.MemType MemType, Int32 memIndex, ref byte Value)
        {
            bool answ = false;
            if (FANUC_ref.Connected)
            {
                try
                {
                    answ = FANUC_ref.F_RW_Byte(bWrite, MemType, memIndex, ref Value);
                }
                catch
                { }
            }
            return answ;
        }
        /// <summary>
        /// wrapper chiamata lettura/scrittura fanuc...
        /// </summary>
        /// <param name="bWrite"></param>
        /// <param name="MemType"></param>
        /// <param name="memIndex"></param>
        /// <param name="MATRICE Value"></param>
        /// <returns></returns>
        public bool FanucMemRW(bool bWrite, FANUC.MemType MemType, Int32 memIndex, ref byte[] Value)
        {
            bool answ = false;
            if (FANUC_ref.Connected)
            {
                try
                {
                    answ = FANUC_ref.F_RW_Byte(bWrite, MemType, memIndex, ref Value);
                }
                catch
                { }
            }
            return answ;
        }

        /// <summary>
        /// struttura dati fanuc x ALL DYN DATA
        /// C:\Users\samuele\Documents\FOCAS A02B-0207-K737_04.20\Document\SpecE\Position\cnc_rddynamic2.htm
        /// </summary>
        protected CMSCncLib.Focas1.ODBDY2_1 allDynData;
        /// <summary>
        /// Vettore degli allarmi CNC attivi
        /// </summary>
        public CMSCncLib.Focas1.ODBALMMSG2 allarmiCNC;

        /// <summary>
        /// estende l'init della classe base...
        /// </summary>
        /// <param name="caller"></param>
        /// <param name="adpConf"></param>
        public AdapterFanuc(MainForm caller, AdapterConf adpConf) : base(caller, adpConf)
        {
            if (utils.CRB("verbose")) lg.Info("Start init Adapter FANUC all'IP {0}", utils.CRS("ipFanuc"));

            Runtime.CreateNC(utils.CRS("ipFanuc"));

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

            FANUC_ref = (FANUC)Runtime.NC;
            if (utils.CRB("verbose")) lg.Info("FANUC_ref da CMSCncLib");

            // disconnetto e connetto...
            if (utils.CRB("verbose")) lg.Info("FANUC: tryDisconnect");
            tryDisconnect();
            if (utils.CRB("verbose")) lg.Info("FANUC: tryConnect");
            tryConnect();
            if (utils.CRB("verbose")) lg.Info("End init Adapter FANUC");
        }

        public override void tryDisconnect()
        {
            if (connectionOk)
            {
                string szStatusConnection = "";
                try
                {
                    FANUC_ref.Disconnect(ref szStatusConnection);
                    connectionOk = false;
                    lg.Info(szStatusConnection);
                    lg.Info("Effettuata disconnessione adapter FANUC!");
                }
                catch (Exception exc)
                {
                    lg.Fatal(exc, "Errore nella disconnessione dall'adapter FANUC");
                }
            }
            else
            {
                lg.Error("IMPOSSIBILE effettuare disconnessione: Connessione non disponibile...");
            }
        }

        public override void tryConnect()
        {
            if (!connectionOk)
            {
                string szStatusConnection = "";
                try
                {
                    FANUC_ref.Connect(ref szStatusConnection);
                    lg.Info(szStatusConnection);
                    connectionOk = true;
                    // refresh stato allarmi!!!
                    if (connectionOk)
                    {
                        // carico status allarmi (completo)
                        lg.Info("Inizio refresh completo stato allarmi...");

                        forceAlarmCheck();

                        lg.Info("Completato refresh completo stato allarmi!");
                    }
                    else
                    {
                        lg.Error("Impossibile procedere, connessione mancante...");
                    }
                }
                catch (Exception exc)
                {
                    lg.Fatal(exc, "Errore nella connessione all'adapter FANUC", szStatusConnection);
                }
            }
        }

        /// <summary>
        /// Verifico connessione fanuc...
        /// </summary>
        /// <returns></returns>
        public override bool connectionOk
        {
            get
            {
                return FANUC_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,
                byte[] MemBlock = new byte[Strobes.Length + Acknowl.Length];
                int memIndex = 10500;
                inizio = DateTime.Now;
                FanucMemRW(R, FANUC.MemType.R, memIndex, ref MemBlock);
                if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-STROBES", MemBlock.Length), DateTime.Now.Subtract(inizio).Ticks);

                // suddivido!
                Buffer.BlockCopy(MemBlock, 0, Acknowl, 0, Acknowl.Length);
                Buffer.BlockCopy(MemBlock, Acknowl.Length, Strobes, 0, Strobes.Length);
            }
            else
            {
                lg.Error("Errore connessione mancante in getStrobeAndAckStatus");
            }
        }

        /// <summary>
        /// processing strobe!
        /// </summary>
        public override void processStrobe()
        {
            base.processStrobe();

            // !!!FARE!!! multipath...
            // verifico i vari strobe x recuperare i dati... PER ORA SOLO DW1 per path1 (indice 0)...
            int idxPath = 0;
            int memIndex = 0;
            int bitNum = 0;
            inizio = DateTime.Now;
            // byte di acknowledge...
            byte[] retACK_DW1 = new byte[4];
            byte[] retACK_DW2 = new byte[4];
            // inizializzo userAction
            string UserAction = "";
            string TestingData = "";
            string UnkStrobe = "";
            // controllo TUTTI i flag: se ce ne sono di alzati DEVO processare...
            if (STRB_DW1 != StFlag32.NONE)
            {
                // blocco memoria x lettura TUTTI i dati di buffer M/S/T: 46 byte: 2byte (16bit) x (11+6+6) aree
                byte[] MemBlock = new byte[46];

                // leggo tutto!!!
                memIndex = 10660;
                inizio = DateTime.Now;
                FanucMemRW(R, FANUC.MemType.R, memIndex, ref MemBlock);
                if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-STRB_DW1", MemBlock.Length), DateTime.Now.Subtract(inizio).Ticks);

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

                // check COD_S
                bitNum = 1;
                gestStrobeCodMST(bitNum, ref retACK_DW1, 11, MemBlock, "S");

                // check COD_T
                bitNum = 2;
                gestStrobeCodMST(bitNum, ref retACK_DW1, 17, MemBlock, "T");

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

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

                // AREA strobe USER ACTION
                // chiamato Start...
                bitNum = 4;
                gestStrobeUserAction(bitNum, ref retACK_DW1, ref UserAction, "# START # ");
                // chiamato Stop...
                bitNum = 5;
                gestStrobeUserAction(bitNum, ref retACK_DW1, ref UserAction, "# STOP # ");
                // chiamato Reset...
                bitNum = 6;
                gestStrobeUserAction(bitNum, ref retACK_DW1, ref UserAction, "# RESET # ");

                // processo tutti gli strobe x i BIT 7-29 NON gestiti in modo da dare comunque ACK e event...
                for (int i = 7; i < 30; i++)
                {
                    gestStrobeUserAction(i, ref retACK_DW1, ref UnkStrobe, string.Format("# STROBE_{0:00} # ", i));
                }

                // AREA strobe x TEST
                // INIZIO TEST...
                bitNum = 30;
                if (STRB_DW1.HasFlag((StFlag32)Math.Pow(2, bitNum)))
                {
                    // formatto stringa risultato
                    TestingData = string.Format("START TEST{0}", getTestData(utils.CRS("testCharSep")));

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

                    // memorizzo allarme nel vettore ack....
                    retACK_DW1 = utils.setBitOnStFlag(retACK_DW1, true, bitNum);
                }
            }
            else
            {
                // se mi sono rimasti degli strobe di lettura allarmi alzati li abbasso
                if (ACK_DW1 != StFlag32.NONE)
                {
                    // inizializzo 4 byte a zero!!!
                    retACK_DW1 = new byte[4];
                }
            }

            // INVIO COMUNQUE vettore azioni (0 o +)...
            mUserAction.Value = UserAction.Trim();

            // Invio comunque strobe non riconosciuti
            mUnkStrobe.Value = UnkStrobe.Trim();

            // INVIO COMUNQUE stato test...
            mTestingData.Value = TestingData.Trim();

            memIndex = 10504;
            // scrivo update ad ack SE VARIATO!!!
            if (ACK_DW1 != (StFlag32)BitConverter.ToUInt32(retACK_DW1, 0))
            {
                inizio = DateTime.Now;
                FanucMemRW(W, FANUC.MemType.R, memIndex, ref retACK_DW1);
                if (utils.CRB("recTime")) TimingData.addResult(string.Format("W{0}-DW1", retACK_DW1.Length), DateTime.Now.Subtract(inizio).Ticks);
            }

            // 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);
                bool setFlag = resto == 0;
                retACK_DW2 = utils.setBitOnStFlag(retACK_DW2, setFlag, 0); // imposto primo bit!!!
                // scrivo su area PLC
                memIndex = 10508;
                inizio = DateTime.Now;
                FanucMemRW(W, FANUC.MemType.R, memIndex, ref retACK_DW2);
                if (utils.CRB("recTime")) TimingData.addResult(string.Format("W{0}-DW2", retACK_DW2.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 byte
            byte[] MemBlockTestData = new byte[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 = 11000;
            inizio = DateTime.Now;
            FanucMemRW(R, FANUC.MemType.R, memIndex, ref MemBlockTestData);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-TEST_DATA_DW1", MemBlockTestData.Length), DateTime.Now.Subtract(inizio).Ticks);

            // recupero cod univoco test e numero parametri impiegati
            testNum = BitConverter.ToUInt16(MemBlockTestData, 0);
            numPar = BitConverter.ToUInt16(MemBlockTestData, 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 = BitConverter.ToUInt32(MemBlockTestData, 4 * (1 + i));
                    answ += string.Format("{0}{1}", charSep, testParam);
                }
            }

            return answ;
        }

        /// <summary>
        /// Gestione STROBE --> ACK per codici M/S/T
        /// </summary>
        /// <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(int bitNum, ref byte[] retACK_DW1, int memShift, byte[] MemBlock, string Coda)
        {
            int numEv = 0;
            int codEv = 0;
            if (STRB_DW1.HasFlag((StFlag32)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 + 1 + 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;
            totRighe = File.ReadLines(fileName).Count();
            // creo un vettore della dimensione corretta... conta anche commenti tanto poi riduco...
            generalStatus = new Dictionary<string, string>();
            // carica da file...
            System.IO.StreamReader file = new System.IO.StreamReader(fileName);
            // 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");


            // 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 gestStrobeUnknown(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>
        /// 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;
            byte[] MemBlock;
            // primo blocco memoria allarmi
            int memIndex = 10532;
            // 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 byte[128];
                // recupero tutti i 32 bit del blocco
                inizio = DateTime.Now;
                FanucMemRW(R, FANUC.MemType.R, memIndex, ref MemBlock);
                if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-STRB_DW0", MemBlock.Length), DateTime.Now.Subtract(inizio).Ticks);

                try
                {
                    // aggiorno vettore allarmi x intero!
                    Buffer.BlockCopy(MemBlock, 0, AlarmFlags, 0, MemBlock.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 byte[4];
                    // 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;
                            FanucMemRW(R, FANUC.MemType.R, memIndex + i * 4, ref MemBlock);
                            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-STRB_DW0", MemBlock.Length), DateTime.Now.Subtract(inizio).Ticks);

                            // da testare metodo copia alternativo !!!FARE!!!
                            if (true)
                            {
                                // aggiorno nel vettore allarmi i byte interessati
                                for (int j = 0; j < 4; j++)
                                {
                                    // copy array o byte?!? !!!FARE!!! verifica
                                    AlarmFlags[i * 4 + j] = MemBlock[j];
                                }
                            }
                            else
                            {
                                Buffer.BlockCopy(MemBlock, i, AlarmFlags, i, MemBlock.Length);
                            }

                            // 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 = 10500;
                        // scrivo update ad ack!!!
                        inizio = DateTime.Now;
                        FanucMemRW(W, FANUC.MemType.R, memIndex, 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 = 10500;
                    // invio INTERO set ACK_DW0 con bit abbassati...
                    retACK_DW0 = BitConverter.GetBytes(Convert.ToUInt32(STRB_DW0 & ACK_DW0));
                    inizio = DateTime.Now;
                    FanucMemRW(W, FANUC.MemType.R, memIndex, 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";
            }

            // HARD CODE: forzo path 1 (indice 0...)
            int idxPath = 0;
            // switch su run mode...
            if (STRB_DW2.HasFlag(StFlag32.B01))
            {
                vettPath[idxPath].mPathRunMode.Value = "AUTOMATIC";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B02))
            {
                vettPath[idxPath].mPathRunMode.Value = "EDIT";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B03))
            {
                vettPath[idxPath].mPathRunMode.Value = "MANUAL_DATA_INPUT";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B04))
            {
                vettPath[idxPath].mPathRunMode.Value = "MANUAL";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B05))
            {
                vettPath[idxPath].mPathRunMode.Value = "MANUAL"; // "JOG";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B07))
            {
                vettPath[idxPath].mPathRunMode.Value = "MANUAL"; //"JOGINC";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B07))
            {
                vettPath[idxPath].mPathRunMode.Value = "MANUAL"; //"HANDLE";
            }

            // switch su EXE mode...
            string exeMode = "";
            if (STRB_DW2.HasFlag(StFlag32.B08))
            {
                exeMode = "ACTIVE";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B09))
            {
                exeMode = "READY";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B10))
            {
                exeMode = "STOPPED"; // "HOLD";
            }
            else if (STRB_DW2.HasFlag(StFlag32.B11))
            {
                exeMode = "FEED_HOLD"; // "FEEDHOLD";
            }
            //// appunto: modi da creare
            //INTERRUPTED(da creare)
            //OPTIONAL_STOP(da creare)
            //PROGRAM_STOPPED(da creare)
            //PROGRAM_COMPLETED(da creare)

            if (vettPath[idxPath].mPathExeMode.Value.ToString() != exeMode)
            {
                vettPath[idxPath].mPathExeMode.Value = exeMode;
            }

            // verifico tipo path...
            if (STRB_DW2.HasFlag(StFlag32.B12))
            {
                vettPath[idxPath].mPathType.Value = "LAVORO";
            }
            else
            {
                vettPath[idxPath].mPathType.Value = "ASSERV";
            }

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

            // processo eventuali altri bit status non noti...
            string UnkStatus = "";
            // cerco i bit alzati --> aggiungo in stringa!
            for (int i = 14; i < 32; i++)
            {
                // converto! e aggiungo allarmi sollevati al corretto controller allarmi...
                if (STRB_DW2.HasFlag((StFlag32)Math.Pow(2, i)))
                {
                    UnkStatus += string.Format("# STATUS_{0:00} # ", i);
                }
            }
            // invio comunque...
            mUnkStatus.Value = UnkStatus.Trim();

            // 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("# {0} #", elencoSubMode[i.ToString()]);
                    }
                }

            }
            // INVIO SEMBRE (x prendere il reset/fronte discesa)...
            vettPath[idxPath].mPathSubMode.Value = SubMode.Trim();
        }

        public override void getConfigParam()
        {
            base.getConfigParam();
        }

        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_Fanuc(currAdpConf.nUnOp);

        }
        /// <summary>
        /// Gestione lettura dati manutenzione
        /// </summary>
        private void getMtzDataFromPlc()
        {
            bool needSave = false;
            // recupero i dati di manutenzione dall'area di memoria IN BLOCCO
            int memIndex = 14000;
            byte[] tabDatiMtz = new byte[4 * maintData.Length];
            inizio = DateTime.Now;
            FanucMemRW(R, FANUC.MemType.R, memIndex, ref tabDatiMtz);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-DatiMtz", tabDatiMtz.Length), DateTime.Now.Subtract(inizio).Ticks);
            // uno ad uno vado a inserirli nella mappa dei dati dell'adapter...
            int shift = 4; // 32bit
            int numero = 0;
            uint valRead = 0;
            for (int i = 0; i < maintData.Length; i++)
            {
                numero = 0;
                valRead = 0;
                // 2016.07.06 uso indice i * 4 x selezionare area memoria...
                //shift = Convert.ToInt32(maintData[i].codNum) - 1;

                if (maintData[i].varName == "ACC_TIME")
                {
                    istOreMaccOn = BitConverter.ToUInt32(tabDatiMtz, shift * i);
                }
                else if (maintData[i].varName == "ACC_TIME_WORK")
                {
                    istOreMaccLav = BitConverter.ToUInt32(tabDatiMtz, shift * i);
                }
                else if (maintData[i].varName.StartsWith("Path_"))
                {
                    if (maintData[i].varName.EndsWith("_PZ_TOT"))
                    {
                        try
                        {
                            numero = Convert.ToInt32(maintData[i].varName.Replace("Path_", "").Replace("_PZ_TOT", ""));
                        }
                        catch
                        {
                        }
                        if (numero > 0)
                        {
                            vettPath[numero - 1].mPathPartCount.Value = BitConverter.ToUInt32(tabDatiMtz, shift * i);
                        }
                    }
                }
                else if (maintData[i].varName.StartsWith("Axis_"))
                {
                    if (maintData[i].varName.EndsWith("_DistDone"))
                    {
                        try
                        {
                            numero = Convert.ToInt32(maintData[i].varName.Replace("Axis_", "").Replace("_DistDone", ""));
                        }
                        catch
                        { }
                        if (numero > 0)
                        {
                            istDistMovAssi[numero - 1] = BitConverter.ToUInt32(tabDatiMtz, shift * i);
                        }
                    }
                    else if (maintData[i].varName.EndsWith("_Invers"))
                    {
                        try
                        {
                            numero = Convert.ToInt32(maintData[i].varName.Replace("Axis_", "").Replace("_Invers", ""));
                        }
                        catch
                        { }
                        if (numero > 0)
                        {
                            // 2016.07.06 debug di valori che potrebbero essere nulli...
                            try
                            {
                                valRead = BitConverter.ToUInt32(tabDatiMtz, shift * i);
                            }
                            catch (Exception exc)
                            {
                                valRead = 0;
                                lg.Error(string.Format("Errore in recupero num inversioni:{0}parametro: {1}{0}shift: {2}{0}i: {3}{0}Exc: {4}", Environment.NewLine, maintData[i].varName, shift, i, exc));
                            }
                            istNumInvAssi[numero - 1] = valRead;
                        }
                    }
                    else if (maintData[i].varName.EndsWith("_AccTime"))
                    {
                        try
                        {
                            numero = Convert.ToInt32(maintData[i].varName.Replace("Axis_", "").Replace("_AccTime", ""));
                        }
                        catch
                        { }
                        if (numero > 0)
                        {
                            // 2016.07.06 debug di valori che potrebbero essere nulli...
                            try
                            {
                                valRead = BitConverter.ToUInt32(tabDatiMtz, shift * i);
                            }
                            catch (Exception exc)
                            {
                                valRead = 0;
                                lg.Error(string.Format("Errore in recupero num inversioni:{0}parametro: {1}{0}shift: {2}{0}i: {3}{0}Exc: {4}", Environment.NewLine, maintData[i].varName, shift, i, exc));
                            }
                            istAccTimeAssi[numero - 1] = valRead;
                        }
                    }
                }
                else if (maintData[i].varName.StartsWith("UnOp_"))
                {
                    if (maintData[i].varName.EndsWith("_AccTime"))
                    {
                        try
                        {
                            numero = Convert.ToInt32(maintData[i].varName.Replace("UnOp_", "").Replace("_AccTime", ""));
                        }
                        catch
                        { }
                        if (numero > 0)
                        {
                            istGiriElettrom[numero - 1] = BitConverter.ToUInt32(tabDatiMtz, shift * i);
                        }
                    }
                    else if (maintData[i].varName.EndsWith("_NumCambiUT"))
                    {
                        try
                        {
                            numero = Convert.ToInt32(maintData[i].varName.Replace("UnOp_", "").Replace("_NumCambiUT", ""));
                        }
                        catch
                        { }
                        if (numero > 0)
                        {
                            istNumCambiUt[numero - 1] = BitConverter.ToUInt32(tabDatiMtz, shift * i);
                        }
                    }
                }
                else if (maintData[i].varName.StartsWith("VacPump_"))
                {
                    if (maintData[i].varName.EndsWith("_WrkTime"))
                    {
                        try
                        {
                            numero = Convert.ToInt32(maintData[i].varName.Replace("VacPump_", "").Replace("_WrkTime", ""));
                        }
                        catch
                        { }
                        if (numero > 0)
                        {
                            istVacPumpWrkTime[numero - 1] = BitConverter.ToUInt32(tabDatiMtz, shift * i);
                        }
                    }
                }
                else if (maintData[i].varName.StartsWith("VacAct_"))
                {
                    if (maintData[i].varName.EndsWith("_Count"))
                    {
                        try
                        {
                            numero = Convert.ToInt32(maintData[i].varName.Replace("VacAct_", "").Replace("_Count", ""));
                        }
                        catch
                        { }
                        if (numero > 0)
                        {
                            istVacActCount[numero - 1] = BitConverter.ToUInt32(tabDatiMtz, shift * i);
                        }
                    }
                }
                else if (maintData[i].varName.StartsWith("Lubro_"))
                {
                    if (maintData[i].varName.EndsWith("_Num"))
                    {
                        try
                        {
                            numero = Convert.ToInt32(maintData[i].varName.Replace("Lubro_", "").Replace("_Num", ""));
                        }
                        catch
                        { }
                        if (numero > 0)
                        {
                            istLubroCount[numero - 1] = BitConverter.ToUInt32(tabDatiMtz, shift * i);
                        }
                    }
                }
            }

            // verifica se si debba aggiornare XML (e salva in adapter dati vari)
            needSave = procOreMaccOn(needSave);
            needSave = procOreMaccLav(needSave);
            needSave = procProgrName(needSave);
            needSave = procPartId(needSave);
            needSave = procPzProd(needSave);
            needSave = procGiriTotUnOp(needSave);
            needSave = procNumCU(needSave);
            needSave = procMovTotAssi(needSave);
            needSave = procAccTimeAssi(needSave);
            needSave = procNumInvAssi(needSave);
            needSave = procVacPump(needSave);
            needSave = procVacAct(needSave);
            needSave = procLubro(needSave);

            // salvo se necessario!
            if (needSave) parentForm.persistData();
            // -------------------------------------------

        }

        /// <summary>
        /// Gestione lettura dati status da PLC
        /// </summary>
        private void getStatusDataFromPlc()
        {
            // recupero i dati di manutenzione dall'area di memoria IN BLOCCO
            int memIndex = 14512;
            int bitNum = 0;
            int byteNum = 0;
            byte byteVal;
            int numByte = 1 + (statusData.Length / 8);
            byte[] tabDatiStatus = new byte[numByte];
            inizio = DateTime.Now;
            FanucMemRW(R, FANUC.MemType.R, memIndex, ref tabDatiStatus);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-DatiStatus", tabDatiStatus.Length), DateTime.Now.Subtract(inizio).Ticks);
            // uno ad uno vado a inserirli nella mappa dei dati dell'adapter...
            int numero = 0;
            string status = "";
            for (int i = 0; i < statusData.Length; i++)
            {
                numero = 0;
                status = "";

                // calcolo quale byte e quale bit devo leggere..
                byteNum = i / 8;
                bitNum = i - (8 * byteNum); // indice zero dei bit nel byte ( da cui -1 )
                // faccio vera lettura        
                byteVal = tabDatiStatus[byteNum];
                // leggo bit come ACTIVE/INACTIVE
                if (((StFlag8)byteVal).HasFlag((StFlag8)Math.Pow(2, bitNum)))
                {
                    status = "ACTIVE";
                }
                else
                {
                    status = "INACTIVE";
                }

                if (statusData[i].varName == "PROTECTION_STATUS")
                {
                    mProtectionStatus.Value = status;
                }
                else if (statusData[i].varName.StartsWith("VacPump_"))
                {
                    if (statusData[i].varName.EndsWith("_Status"))
                    {
                        try
                        {
                            numero = Convert.ToInt32(statusData[i].varName.Replace("VacPump_", "").Replace("_Status", ""));
                        }
                        catch
                        {
                        }
                        if (numero > 0)
                        {
                            vettVacPump[numero - 1].mVacPumpStatus.Value = status;
                        }
                    }
                }
                else if (statusData[i].varName.StartsWith("Cooler_"))
                {
                    if (statusData[i].varName.EndsWith("_Status"))
                    {
                        try
                        {
                            numero = Convert.ToInt32(statusData[i].varName.Replace("Cooler_", "").Replace("_Status", ""));
                        }
                        catch
                        { }
                        if (numero > 0)
                        {
                            vettCooler[numero - 1].mCoolStatus.Value = status;
                        }
                    }
                }
                else if (statusData[i].varName.StartsWith("UnOp_"))
                {
                    if (statusData[i].varName.EndsWith("_Status"))
                    {
                        try
                        {
                            numero = Convert.ToInt32(statusData[i].varName.Replace("UnOp_", "").Replace("_Status", ""));
                        }
                        catch
                        { }
                        if (numero > 0)
                        {
                            vettUnOp[numero - 1].mUnOpStatus.Value = status;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Leggo dati globali comuni (x path, assi...)
        /// </summary>
        public override void getGlobalData()
        {
            base.getGlobalData();

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

            // leggo dati globali...
            inizio = DateTime.Now;
            allDynData = FANUC_ref.getAllDynData();
            if (utils.CRB("recTime")) TimingData.addResult("R-ALL-DYN-DATA", DateTime.Now.Subtract(inizio).Ticks);

            FeedRate = allDynData.actf;

            int memIndex = 12000;
            byte[] PathData = new byte[4];
            inizio = DateTime.Now;
            FanucMemRW(R, FANUC.MemType.R, memIndex, ref PathData);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-PathData", PathData.Length), DateTime.Now.Subtract(inizio).Ticks);

            // 2016.07.19 mod con Fabio
            //JogRateOver = PathData[0];
            FeedRateOver = PathData[1];
            RapidOver = PathData[2];
            SpeedRateOver = PathData[3];

            sb.AppendLine(string.Format("FeedRate: {0} mm/min", FeedRate));
            sb.AppendLine(string.Format("FeedRateOver: {0} %", FeedRateOver));
            sb.AppendLine(string.Format("RapidOver: {0} %", RapidOver));
            sb.AppendLine(string.Format("SpeedRateOver: {0} %", SpeedRateOver));

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

                inizio = DateTime.Now;
                allarmiCNC = FANUC_ref.getCncAlarm();
                if (utils.CRB("recTime")) TimingData.addResult("R-CNC-ALARMS", DateTime.Now.Subtract(inizio).Ticks);
                checkCNCAlarms();
            }
            else
            {
                allarmiCNC = new CMSCncLib.Focas1.ODBALMMSG2();
            }

            parentForm.dataMonitor = sb.ToString();

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

            // gestisco lettura dati status da PLC...
            getStatusDataFromPlc();
        }
        /// <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"), "R", 14000, 4);
                    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...
            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, ':');
                    statusData[numRiga] = decodeBitData(linea, utils.CRC("testCharSep"), "R", 14512, 1, 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));
        }

        public override void processAlarm()
        {
            base.processAlarm();

            // ggiungo gestione allarmi CNC
            checkCNCAlarms();
        }

        /// <summary>
        /// Verifica i 10 allarmi CNC SE presenti
        /// </summary>
        private void checkCNCAlarms()
        {
            // faccio parse allarmi: se ci sono invio e presento
            sendAlarmIfPresent(allarmiCNC.msg1);
            sendAlarmIfPresent(allarmiCNC.msg2);
            sendAlarmIfPresent(allarmiCNC.msg3);
            sendAlarmIfPresent(allarmiCNC.msg4);
            sendAlarmIfPresent(allarmiCNC.msg5);
            sendAlarmIfPresent(allarmiCNC.msg6);
            sendAlarmIfPresent(allarmiCNC.msg7);
            sendAlarmIfPresent(allarmiCNC.msg8);
            sendAlarmIfPresent(allarmiCNC.msg9);
            sendAlarmIfPresent(allarmiCNC.msg10);
        }

        /// <summary>
        /// Invia singolo allarme CNC se presente
        /// </summary>
        /// <param name="allarme"></param>
        protected void sendAlarmIfPresent(CMSCncLib.Focas1.ODBALMMSG2_data allarme)
        {
            // controllo valore...
            if (allarme.alm_no > 0)
            {
                //if (utils.CRB("verbose")) sb.AppendLine(string.Format("Allarmi CNC: NUM {0} | MESS: {1} | ASSE: {2} | TYPE: {3}", allarme.alm_no, allarme.alm_msg, allarme.axis, allarme.type));

                // se ho asse accodo ad asse altrimenti no...
                string codAllarme = string.Format("T{0}-N{1}", allarme.type, allarme.alm_no);
                if (allarme.axis == 0)
                {
                    mAlarmCNC.Add(MTConnect.Condition.Level.FAULT, allarme.alm_msg, codAllarme, "", "");
                }
                else
                {
                    mAlarmCNC.Add(MTConnect.Condition.Level.FAULT, allarme.alm_msg, codAllarme, "", "");
                    vettAxis[allarme.axis - 1].mAxAlarmCNC.Add(MTConnect.Condition.Level.FAULT, allarme.alm_msg, codAllarme, "", "");
                }
            }
        }

        public override void getUnOp()
        {
            base.getUnOp();

            // 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;
            FanucMemRW(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);

            // NON VA: RICHIEDE OPZIONE... The extended driver/library function is necessary. 
#if false
            // recupero vettore load spindle...
            inizio = DateTime.Now;
            CMSCncLib.Focas1.ODBSPN spindleVect = FANUC_ref.getSpindleLoad();
            if (utils.CRB("recTime")) TimingData.addResult("R-SpindleLoad", DateTime.Now.Subtract(inizio).Ticks); 
#endif
            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();
        }
        /// <summary>
        /// procedura di lettura (Multipla) dati da memoria x caricare dati utensile
        /// </summary>
        /// <param name="maxNumOp">num max di teste da recuperare</param>
        private void getDatiUt_Fanuc(int maxNumOp)
        {
            // variabili "Indice" utensile e famiglia da decodificare + utils
            int maxMemAddr = 200;
            int[] idUtMontato = new int[maxNumOp];
            int[] idFamUt = new int[maxNumOp];
            int[] idTipoVitaUt = new int[maxNumOp];
            int[] valVitaUtRes = new int[maxNumOp];
            int[] tabUt = new int[maxMemAddr];
            int[] tabFamUt = new int[maxMemAddr];
            int memIndex = 0;
            byte[] int32Mem = new byte[4];
            byte[] int16Mem = new byte[2];
            byte int8Mem = new byte();

            // step 1: lettura ID dell'utensile x ogni testa...
            byte[] elencoUtMem = new byte[2 * maxNumOp];
            // leggo area memoria degli UT montati
            memIndex = 2960;
            inizio = DateTime.Now;
            FanucMemRW(R, FANUC.MemType.D, memIndex, ref elencoUtMem);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-NUMUT", elencoUtMem.Length), DateTime.Now.Subtract(inizio).Ticks);
            // decodifica da byte a int16 dell'ID Utensile
            for (int i = 0; i < maxNumOp; i++)
            {
                idUtMontato[i] = BitConverter.ToUInt16(elencoUtMem, 2 * i);
            }

            // step 2a: leggo vettore memoria dei maxMemAddr(200) UT in Tabella Utensili
            memIndex = 11300;
            byte[] tabUt_UT = new byte[2 * maxMemAddr];
            inizio = DateTime.Now;
            FanucMemRW(R, FANUC.MemType.D, memIndex, ref tabUt_UT);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-TabUT-UT", tabUt_UT.Length), DateTime.Now.Subtract(inizio).Ticks);
            // decodifico TUTTI i maxMemAddr utensili
            for (int i = 0; i < maxMemAddr; i++)
            {
                tabUt[i] = BitConverter.ToUInt16(tabUt_UT, 2 * i);
            }
            // step 2a: devo cercare l'UT di ogni UnOp nella Tabella Utensili
            for (int i = 0; i < maxNumOp; i++)
            {
                // spazzo la tab Utensili x cercare quelli delle UnOp...
                for (int j = 0; j < maxMemAddr; j++)
                {
                    // controllo, se l'id utensile è quello dell'attuale UnOp...
                    if (tabUt[j] == idUtMontato[i])
                    {
                        // leggo FamUt puntuale..
                        memIndex = 11700 + 2 * j;
                        inizio = DateTime.Now;
                        FanucMemRW(R, FANUC.MemType.D, memIndex, ref int16Mem);
                        if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-TabUT-FamUT", int16Mem.Length), DateTime.Now.Subtract(inizio).Ticks);
                        // step 2b: leggo puntualmente la FamUt dal suo indice...
                        idFamUt[i] = BitConverter.ToUInt16(int16Mem, 0);
                    }
                }
            }


            // step 3: leggo vettore memoria dei maxMemAddr(200) FamUT in Tabella Famiglie Utensili (x ricavare unità misura vita)
            memIndex = 8900;
            byte[] tabFam_FamUt = new byte[2 * maxMemAddr];
            inizio = DateTime.Now;
            FanucMemRW(R, FANUC.MemType.D, memIndex, ref tabFam_FamUt);
            if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-TabFamUT-FamUT", tabFam_FamUt.Length), DateTime.Now.Subtract(inizio).Ticks);
            // decodifico TUTTI i maxMemAddr utensili
            for (int i = 0; i < maxMemAddr; i++)
            {
                tabFamUt[i] = BitConverter.ToUInt16(tabFam_FamUt, 2 * i);
            }
            // step 3a: devo cercare la famiglia dell'UT di ogni UnOp nella Tabella Famiglie
            for (int i = 0; i < maxNumOp; i++)
            {
                // poiché potrei trovarlo replicato dopo la prima volta che lo trovo evito di ricontrollare...
                bool found = false;
                // spazzo la tab Utensili x cercare quelli delle UnOp...
                for (int j = 0; j < maxMemAddr; j++)
                {
                    // controllo, se l'id utensile è quello dell'attuale UnOp...
                    if (tabFamUt[j] == idFamUt[i] && !found)
                    {
                        // leggo FamUt puntuale..
                        memIndex = 10700 + 1 * j;
                        inizio = DateTime.Now;
                        FanucMemRW(R, FANUC.MemType.D, memIndex, ref int8Mem);
                        if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-TabUT-FamUT", 8), DateTime.Now.Subtract(inizio).Ticks);
                        // step 3b: leggo puntualmente il TIPO DI vita UT dal suo indice...
                        idTipoVitaUt[i] = int8Mem;
                        found = true;
                    }
                }
            }

            // step 4: recupero la vita residua dell'utente
            for (int i = 0; i < maxNumOp; i++)
            {
                // spazzo la tab Utensili x cercare quelli delle UnOp...
                for (int j = 0; j < maxMemAddr; j++)
                {
                    // controllo, se l'id utensile è quello dell'attuale UnOp...
                    if (tabUt[j] == idUtMontato[i])
                    {
                        // leggo FamUt puntuale..
                        memIndex = 13100 + 4 * j;
                        inizio = DateTime.Now;
                        FanucMemRW(R, FANUC.MemType.D, memIndex, ref int32Mem);
                        if (utils.CRB("recTime")) TimingData.addResult(string.Format("R{0}-TabUT-VitaRes", int32Mem.Length), DateTime.Now.Subtract(inizio).Ticks);
                        // leggo puntualmente valore della vita ut residua da indice utensile...
                        valVitaUtRes[i] = BitConverter.ToInt32(int32Mem, 0);
                    }
                }
            }

            // salvo in adapter vita UT residua...
            for (int i = 0; i < maxNumOp; i++)
            {
                vettUnOp[i].mUnOpToolId.Value = idUtMontato[i];
                vettUnOp[i].mUnOpVitaRes.Value = valVitaUtRes[i];
                // decodifico valore tipo VitaRes...
                string tipoVitaRes = "ND";
                switch (idTipoVitaUt[i])
                {
                    case 1:
                        tipoVitaRes = "Time";
                        break;
                    case 2:
                        tipoVitaRes = "Stroke";
                        break;
                    case 3:
                        tipoVitaRes = "Distance";
                        break;
                    default: break;
                }
                vettUnOp[i].mUnOpVitaResType.Value = tipoVitaRes;
            }
        }

        public override void getPath()
        {
            base.getPath();

            // recupero vettore generale G MODE
            inizio = DateTime.Now;
            CMSCncLib.Focas1.ODBGCD vettGCodes = FANUC_ref.getPathGCodeMod();
            if (utils.CRB("recTime")) TimingData.addResult("R-GCodModal", DateTime.Now.Subtract(inizio).Ticks);

            string GCodAttivi = "";
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd0.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd1.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd2.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd3.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd4.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd5.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd6.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd7.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd8.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd9.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd10.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd11.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd12.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd13.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd14.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd15.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd16.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd17.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd18.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd19.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd20.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd21.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd22.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd23.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd24.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd25.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd26.code);
            GCodAttivi += string.Format("# {0} #", vettGCodes.gcd27.code);

            inizio = DateTime.Now;
            vettGCodes = FANUC_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);

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

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

                vettPath[i].mPathCodG_Act.Value = GCodAttivi;

                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...

                //vettPath[i].mPathCurrProgRowNum.Value = PtData.ProgrRow;
                //vettPath[i].mPathActiveAxes.Value = PtData.ActiveAxes;

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

                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")));

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

        }

        public override void getAxis()
        {

            // mostro assi in DataMonitor......
            StringBuilder sb = new StringBuilder();

            inizio = DateTime.Now;
            CMSCncLib.Focas1.ODBPOS posAssi = FANUC_ref.getAllAxisPos();
            if (utils.CRB("recTime")) TimingData.addResult("R-AXIS_POS", DateTime.Now.Subtract(inizio).Ticks);

            inizio = DateTime.Now;
            CMSCncLib.Focas1.ODBSVLOAD loadAssi = FANUC_ref.getAllAxisLoad();
            if (utils.CRB("recTime")) TimingData.addResult("R-AXIS_LOAD", DateTime.Now.Subtract(inizio).Ticks);

            CMSCncLib.Focas1.LOADELM valLoad = new CMSCncLib.Focas1.LOADELM();
            CMSCncLib.Focas1.POSELMALL valPos = new CMSCncLib.Focas1.POSELMALL();

            // nuova posizione (per calcoli)
            double newPos = 0;
            double distPerc = 0;
            int newDir = 0;
            string tipoAsse = "";
            string direzione = "";

            for (int i = 0; i < currAdpConf.nAxis; i++)
            {

                // in base a indice scelgo valore posizione e load
                switch (i)
                {
                    case 0:
                        valPos = posAssi.p1;
                        valLoad = loadAssi.svload1;
                        break;
                    case 1:
                        valPos = posAssi.p2;
                        valLoad = loadAssi.svload2;
                        break;
                    case 2:
                        valPos = posAssi.p3;
                        valLoad = loadAssi.svload3;
                        break;
                    case 3:
                        valPos = posAssi.p4;
                        valLoad = loadAssi.svload4;
                        break;
                    case 4:
                        valPos = posAssi.p5;
                        valLoad = loadAssi.svload5;
                        break;
                    case 5:
                        valPos = posAssi.p6;
                        valLoad = loadAssi.svload6;
                        break;
                    default:
                        valPos = posAssi.p1;
                        valLoad = loadAssi.svload1;
                        break;
                }

                // popolo valori...
                vettAxis[i].mAxLoad.Value = (double)(valLoad.data) / Math.Pow(10, valLoad.dec);
                newPos = (double)(valPos.abs.data) / Math.Pow(10, valPos.abs.dec);
                vettAxis[i].mAxPosAct.Value = newPos;
                vettAxis[i].mAxPosTgt.Value = newPos + (double)(valPos.dist.data) / Math.Pow(10, valPos.dist.dec);

                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}, ToGo:{2:N3}{3} | {4}", valPos.abs.name, (double)(valPos.abs.data) / Math.Pow(10, valPos.abs.dec), valPos.dist.name, (double)(valPos.dist.data) / Math.Pow(10, valPos.dist.dec), 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;
            }

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