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

namespace IOB_WIN_SIEMENS.IobSiemens
{
    /// <summary>
    /// Controllo Siemens specifico x impianti Ingenia (Automazione Valvital)
    /// </summary>
    public class SiemensIngenia : Siemens
    {
        /* --------------------------------------------------------------------------------
         * Controlli SIEMENS INGENIA (system integrator gestione robot impianto PRESSA in VALVITAL)
         * - basasto su SIEMENS
         * - S7 vers 1500
         *
         * STRUTTURA MEMORIA DB700
         *
         * Le informazioni di cui abbiamo bisogno (lettura) sono le seguenti :
         * Macchina Lavora (DB700.DBX0.0)
         * Macchina in ciclo (DB700.DBX0.1)
         * Macchina in manuale (DB700.DBX0.2)
         * Macchina in allarme (DB700.DBX0.3)
         * Macchina in emergenza (DB700.DBX0.4)
         * Macchina in timeout -> NON GESTIBILE in quanto attualmente gestito come ALLARME o SCARTO.
         * Contapezzi
         * Gestione scarti
         *
         * Sul tema del contapezzi ovviamente ci adattiamo a come gestite la cosa e quindi vanno bene sia 2 (o più) contapezzi (contapezzi_buono, contapezzi_scarto_forno, contapezzi_scarto_pressa, contapezzi_scarto_bilancere) -> VITA ISOLA (DB700.DBD24), BUONI (DB700.DBD28), SCARTO SAET (DB700.DBD32), SCARTO PEZZI FREDDI (DB700.DBD36).
         *
         * in scrittura vorremmo indicare al PLC
         * - codice articolo(DB701.DBD20)
         * - codice commessa(DB701.DBD24)
         * - quantità produzione(DB701.DBD28)
         *
         * Questi 3 dati devono essere visualizzati in una qualche schermata per poter asseverare il requisito della 4.0, non è necessario ci siano vincoli per cui se ad esempio la richiesta fosse superata il robot continua a lavorare comunque.
         *
         *
         * NB: Si intende tutto con DB700.DBxx
         *
         * - BIT di stato
         *   - DBX0.0: Macchina in LAVORAZIONE
         *   - DBX0.1: Macchina in CICLO
         *   - DBX0.2: Macchina in MANUALE
         *   - DBX0.3: Macchina in ALLARME
         *   - DBX0.4: Macchina in EMERGENZA
         *
         *   - DBD24:  Vita isola (!!!???) Contapezzi TOTALISSIMO?
         *   - DBD28:  BUONI
         *   - DBD32:  pezzi prelevati per controllo qualità (era SCARTO SAET)
         *   - DBD36:  SCARTO pezzi freddi
         *
         *
         *   SCRITTURA
         *   - DBD701.DBD20: codice articolo
         *   - DBD701.DBD24: codice commessa
         *   - DBD701.DBD28: qta produzione
         *
         * -------------------------------------------------------------------------------- */

        #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 SiemensIngenia(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
        {
            lgInfo("NEW IOB SIEMENS versione VIPA INGENIA");
            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: dovrebbe togliere SOLO i task eseguiti...
            Dictionary<string, string> taskDone = new Dictionary<string, string>();
            bool taskOk = false;
            string taskVal = "";
            // inizio con 1 byte VUOTO
            byte[] MemBlock = new byte[1];
            string memAddrWrite = "";
            if (task2exe != null)
            {
                // cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4
                foreach (var item in task2exe)
                {
                    taskOk = false;
                    taskVal = "";
                    memAddrWrite = "";
                    // converto richiesta in enum...
                    taskType tName = taskType.nihil;
                    Enum.TryParse(item.Key, out tName);
                    // controllo sulla KEY
                    switch (tName)
                    {
                        case taskType.nihil:
                        case taskType.fixStopSetup:
                        case taskType.forceResetPzCount:
                        case taskType.forceSetPzCount:
                        case taskType.setProg:
                        case taskType.startSetup:
                        case taskType.stopSetup:
                            taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
                            break;

                        case taskType.sendWatchDogMes2Plc:
                            memAddrWrite = "DB701.DBB12";
                            upsertKey(item.Key, item.Value);
                            MemBlock = dwordToByte(counterMes2Plc.ToString());
                            taskVal = $"VALUE DB701.DBB12  --> {counterMes2Plc} ({MemBlock})";
                            if (verboseLog)
                            {
                                lgInfo(taskVal);
                            }
                            break;

                        case taskType.setArt:
                        case taskType.setComm:
                        case taskType.setPzComm:
                        case taskType.setParameter:
                            upsertKey(item.Key, item.Value);
                            // 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}";
                            }
                            // 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";
                                }
                            }
                            taskOk = true;
                            break;

                        default:
                            taskVal = "SKIPPED | NO EXEC";
                            break;
                    }
                    // aggiungo task!
                    taskDone.Add(item.Key, taskVal);
                    if (!string.IsNullOrEmpty(memAddrWrite))
                    {
                        // scrivo!
                        taskOk = S7WriteBB(ref MemBlock, memAddrWrite);
                    }
                    if (!taskOk)
                    {
                        lgError($"Ingenia: errore in S7WriteBB durante executeTasks: {item.Key} | {item.Value} | memAddrWrite: {memAddrWrite} | {taskVal}");
                    }
                }
            }
            return taskDone;
        }

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

        public override void processWhatchDog()
        {
            // scrivo 1 volta al secondo il contatore incrementale su area apposita
            DateTime adesso = DateTime.Now;
            if (adesso.Subtract(lastPLCWatchDog).TotalSeconds > watchDogPeriod)
            {
                // incremento
                counterMes2Plc++;
                // se > 999'999'999 --> 0
                if (counterMes2Plc > 999999999) 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, "");
                lgInfo($"Scritto watchdog: {counterMes2Plc}");
                // salvo watchdog PLC
                lastPLCWatchDog = adesso;
            }
        }

        #endregion Public Methods

        #region Protected Fields

        protected int counterMes2Plc = 0;

        protected int counterPlc2Mes = 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: emergenza
             *
             *
           * - BIT di stato
           *   - DBX0.0: Macchina in LAVORAZIONE
           *   - DBX0.1: Macchina in CICLO
           *   - DBX0.2: Macchina in MANUALE
           *   - DBX0.3: Macchina in ALLARME
           *   - DBX0.4: Macchina in EMERGENZA
             ----------------------------------------------------- */

            // bit 0 (poweron) imposto a 1 SE connected...
            B_input = currPLC.IsConnected ? 1 : 0;

            // recupero byte segnali...
            byte mainData = RawInput[0];
            int byteSignals = 0;

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

            // RUN se lavora ed in ciclo
            if ((mainData & (1 << 0)) != 0 && (mainData & (1 << 1)) != 0)
            {
                byteSignals += (1 << 1);
            }
            // manuale
            if ((mainData & (1 << 2)) != 0)
            {
                byteSignals += (1 << 4);
            }
            // allarme
            if ((mainData & (1 << 3)) != 0)
            {
                byteSignals += (1 << 3);
            }
            // emergenza
            if ((mainData & (1 << 4)) != 0)
            {
                byteSignals += (1 << 5);
            }

            // salvo!
            B_input = byteSignals;

            // log opzionale!
            if (verboseLog)
            {
                lgInfo(string.Format($"Trasformazione dati: RawInput:{RawInput[3]} --> B_input: {B_input}"));
            }
        }

        #endregion Protected Methods
    }
}