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

namespace IOB_WIN_SIEMENS.IobSiemens
{
    /// <summary>
    /// Controllo Siemens specifico x impianti SAET
    /// </summary>
    public class SiemensSaet : Siemens
    {
        /* --------------------------------------------------------------------------------
         * Controlli SIEMENS SAET (impianti ad induzione in VALVITAL)
         * - basasto su SIEMENS
         * - S7 vers 1500
         *
         * STRUTTURA MEMORIA DB1275: primi 92 byte lettura, poi 56 byte scrittura, vedere doc allegato
         * G:\Drive condivisi\30_Clienti\Valvital\Comunicazione PLC\SAET (forno e tempra)
         *
         * Si intende tutto con DB1275.DBxx
         *
         * - parametri processo
         *   - DBD00: Watchdog	INT	SAET Alive ( 1-9999 )
         *
         *   - DB60.DBD6: pressione camera filtrante (salvataggio del MAX ogni minuto) | var testVal = S7.Net.Types.Double.FromByteArray(memByteRead.Skip(0).Take(4).ToArray());
         *   - DB60.DBD10: pressione linea utenze (salvataggio del MAX ogni minuto)
         *   - DB60.DBD14: temperatura acqua pulita (salvataggio del MAX ogni minuto)
         *
         * - BIT di stato
         *   - DBX2.1: READY TO RUN in AUTOMATICO
         *   - DBX2.3: Macchina in LAVORAZIONE
         *   - DBX2.4: WARNING Differenza tra Part Code MES - Saet (blu)
         *   - DBX2.5: se 1 --> LAMPADA ROSSA (allarmi almeno 1 attivo)
         *
         *  PartCounter	DINT	4.0	Conteggio Parziale di pezzi "OK" prodotti dalla macchina
         *  NumberCode	String [12]	8.0	Valore numerico associato alla ricetta di produzione attualmente utilizzata dalla macchina
         *  NewCode	INT	22.0	"1= Avvenuta ricezione del segnale ""richiesta nuovo ordine di produzione (NEW CODE)""
         *  ricevuto dal server,impostabile su 1  solo quando la macchina NON è in produzione attiva"
         *  Potenza utilizzata ST.1	REAL	24	Potenza utilizzata dalla stazione di riscaldo 1  [kW]
         *  Potenza utilizzata ST.2	REAL	28	Potenza utilizzata dalla stazione di riscaldo 2  [kW]
         *  Potenza utilizzata ST.3	REAL	32	Potenza utilizzata dalla stazione di riscaldo 3  [kW]
         *  Potenza utilizzata ST.4	REAL	36	Potenza utilizzata dalla stazione di riscaldo 4  [kW]
         *  Lettura Pirometro ST.1	REAL	40	Lettura Pirometro della stazione di riscaldo 1  [°C]
         *  Lettura Pirometro ST.2	REAL	44	Lettura Pirometro della stazione di riscaldo 2 [°C]
         *  Lettura Pirometro ST.3	REAL	48	Lettura Pirometro della stazione di riscaldo 3  [°C]
         *  Lettura Pirometro ST.4	REAL	52	Lettura Pirometro della stazione di riscaldo 4  [°C]
         *  Temperatura Acqua Raff Conv. ST.1	REAL	56	Temperarura Acqua di Raffreddamento Convertitore Stazione di Riscaldo 1 [°C]
         *  Temperatura Acqua Raff Conv. ST.2	REAL	60	Temperarura Acqua di Raffreddamento Convertitore Stazione di Riscaldo 2 [°C]
         *  Temperatura Acqua Raff Conv. ST.3	REAL	64	Temperarura Acqua di Raffreddamento Convertitore Stazione di Riscaldo 3 [°C]
         *  Temperatura Acqua Raff Conv. ST.4	REAL	68	Temperarura Acqua di Raffreddamento Convertitore Stazione di Riscaldo 4 [°C]
         *  Part_Status ST.1	INT	72	Stato Pezzo Stazione di Riscaldo 1  (0=Assente 1=Grezzo 10=OK 11=NOK)
         *  Part_Status ST.2	INT	74	Stato Pezzo Stazione di Riscaldo 2  (0=Assente 1=Grezzo 10=OK 11=NOK)
         *  Part_Status ST.3	INT	76	Stato Pezzo Stazione di Riscaldo 3  (0=Assente 1=Grezzo 10=OK 11=NOK)
         *  Part_Status ST.4	INT	78	Stato Pezzo Stazione di Riscaldo 4 (0=Assente 1=Grezzo 10=OK 11=NOK)
         *  Reserve_12	REAL	80	Riserva
         *  Reserve_13	REAL	84	Riserva
         *  Reserve_14	REAL	88	Riserva

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

        #region Public Constructors

        /// <summary>
        /// Classe base con i metodi x Siemens
        /// </summary>
        /// <param name="caller">Form chiamante</param>
        /// <param name="IOBConf">Configurazione (legacy)</param>
        /// <param name="IobConfFull">Configurazione (v 4.x)</param>
        public SiemensSaet(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
        {
            lgInfo("NEW IOB SIEMENS versione SAET");
            lastPLCWatchDog = DateTime.Now.AddMinutes(-1);
        }

        #endregion Public Constructors

        #region Public Methods

        /// <summary>
        /// Processo i task richiesti e li elimino dalla coda 1:1
        /// </summary>
        /// <param name="task2exe"></param>
        public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe, string codTav)
        {
            // Verificare il protocollo: dovrebeb togliere SOLO i task eseguiti...
            Dictionary<string, string> taskDone = new Dictionary<string, string>();
            bool taskOk = false;
            string taskVal = "";
            // verifico non sia null
            if (task2exe != null)
            {
                // inizio con 1 byte di default
                byte[] MemBlock = new byte[1];
                string memAddrWrite = "";
                // cerco task specifici
                foreach (var item in task2exe)
                {
                    taskOk = false;
                    taskVal = "";
                    // converto richiesta in enum...
                    taskType tName = taskType.nihil;
                    Enum.TryParse(item.Key, out tName);
                    // controllo sulla KEY
                    switch (tName)
                    {
                        case taskType.nihil:
                        case taskType.forceResetPzCount:
                        case taskType.forceSetPzCount:
                        case taskType.setProg:
                            taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
                            break;

                        case taskType.setArt:
                            upsertKey(item.Key, item.Value);
                            MemBlock = new byte[34];
                            saveStringOnMemBlock(ref MemBlock, "setArt", 0, 32);
                            memAddrWrite = "DB1275.DBB96";
                            taskVal = item.Value;
                            break;

                        case taskType.setComm:
                            upsertKey(item.Key, item.Value);
                            MemBlock = new byte[14];
                            saveStringOnMemBlock(ref MemBlock, "setComm", 0, 12);
                            memAddrWrite = "DB1275.DBB130";
                            taskVal = item.Value;
                            break;

                        case taskType.setPzComm:
                            // imposto i valori...
                            MemBlock = new byte[4];
                            int numPz = 0;
                            int.TryParse(item.Value, out numPz);
                            MemBlock = S7.Net.Types.DInt.ToByteArray(numPz);
                            memAddrWrite = "DB1275.DBB144";
                            break;

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

                        case taskType.sendWatchDogMes2Plc:
                            MemBlock = new byte[2];
                            MemBlock[1] = (byte)counterMes2Plc;
                            MemBlock[0] = (byte)(counterMes2Plc >> 8);
                            memAddrWrite = "DB1275.DBB92";
                            taskVal = $"VALUE DB1275.92 --> {counterMes2Plc}";
                            break;

                        case taskType.endProd:
                        case taskType.startSetup:
                            // processo scrittura BIT x richiesta nuovo ordine a INIZIO setup --> è DINT!
                            MemBlock = new byte[2];
                            MemBlock[0] = (byte)1;
                            MemBlock[1] = (byte)1;
                            memAddrWrite = "DB1275.DBB94";
                            lgInfo($"Chiamata startSetup | memAddrWrite: {memAddrWrite} | MemBlock: {MemBlock}");
                            break;

                        case taskType.fixStopSetup:
                        case taskType.stopSetup:
                            // processo scrittura BIT x richiesta nuovo ordine a FINE setup
                            MemBlock = new byte[2];
                            MemBlock[0] = (byte)0;
                            MemBlock[1] = (byte)0;
                            memAddrWrite = "DB1275.DBB94";
                            lgInfo($"Chiamata stopSetup | memAddrWrite: {memAddrWrite} | MemBlock: {MemBlock}");
                            break;

                        default:
                            taskVal = "SKIPPED | NO EXEC";
                            break;
                    }
                    // verifico se posso aggiornare valori in memoria...
                    if (memMap != null && memMap.mMapWrite != null)
                    {
                        if (memMap.mMapWrite.ContainsKey(item.Key))
                        {
                            dataConf currMem = memMap.mMapWrite[item.Key];
                            string addr = currMem.memAddr;
                            taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
                            // salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
                            memMap.mMapWrite[item.Key].value = item.Value;
                        }
                        else
                        {
                            taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}";
                        }
                    }
                    else
                    {
                        taskVal = $"NO MemMap found, SET task: {item.Key} --> {item.Value}";
                    }
                    // aggiungo task!
                    taskDone.Add(item.Key, taskVal);
                    // scrivo comunque!
                    taskOk = S7WriteBB(ref MemBlock, memAddrWrite);
                    if (!taskOk)
                    {
                        lgError($"Errore in S7WriteBB durante executeTasks: {item.Key} | {item.Value}");
                    }
                }
            }

            return taskDone;
        }

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

        public override void processWhatchDog()
        {
            // scrivo 1 volta ogni n secondi il contatore incrementale su area apposita
            DateTime adesso = DateTime.Now;
            if (adesso.Subtract(lastPLCWatchDog).TotalSeconds > watchDogPeriod)
            {
                // incremento
                counterMes2Plc++;
                // se > 9999 --> 0
                if (counterMes2Plc > 9999) counterMes2Plc = 0;
                // salvo su DB
                Dictionary<string, string> task2exe = new Dictionary<string, string>();
                Dictionary<string, string> taskDone = new Dictionary<string, string>();
                task2exe.Add("sendWatchDogMes2Plc", counterMes2Plc.ToString());
                taskDone = executeTasks(task2exe, "");
                // salvo watchdog PLC
                lastPLCWatchDog = adesso;
            }
        }

        #endregion Public Methods

        #region Protected Fields

        protected int counterMes2Plc = 0;
        protected int counterPlc2Mes = 0;
        protected int counterPlc2MesWrote = 0;
        protected DateTime lastPLCWatchDog;

        #endregion Protected Fields

        #region Protected Methods

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

            /* -----------------------------------------------------
             * bitmap MAPO STANDARD
             * B0: POWER_ON
             * B1: RUN
             * B2: pzCount
             * B3: allarme
             * B4: manuale
             * B5: anomalia
             *
             *
             * - BIT di stato
             *   - DBX2.0: macchina accesa
             *   - DBX2.1: CICLO AUTO = NON HO ANOMALIE/ALLARMI (0 --> allarme)
             *   - DBX2.2: contapezzi
             *   - DBX2.3: MACCHINA IN CICLO AUTO = LAVORA
             *   - DBX2.4: ERRORE tra part code MES/SAET (blu)
             *   - DBX2.5: Anomalia presente (rosso)
             ----------------------------------------------------- */

            byte mainData = RawInput[2];

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

            // controllo il bit ALLARME
            if ((mainData & (1 << 1)) == 0)
            {
                byteSignals += (1 << 3);
            }

            // controllo il bit ANOMALIA
            if ((mainData & (1 << 5)) != 0)
            {
                byteSignals += (1 << 3);
            }

            // considero NON auto come manuale...
            if ((mainData & (1 << 3)) == 0)
            {
                byteSignals += (1 << 4);
            }

            // salvo!
            B_input = byteSignals;

            // processo il watchdog!
            counterPlc2Mes = S7.Net.Types.Int.FromByteArray(RawInput.Skip(0).Take(2).ToArray());
            // ogni 60 registro...
            if (Math.Abs(counterPlc2Mes - counterPlc2MesWrote) > 60)
            {
                lgInfo($"WatchDog da PLC: {counterPlc2Mes}");
                counterPlc2MesWrote = counterPlc2Mes;
            }
            // log opzionale!
            if (verboseLog)
            {
                lgInfo($"Trasformazione dati: RawInput:{RawInput[3]} --> B_input: {B_input}");
            }
        }

        #endregion Protected Methods
    }
}