using IOB_UT_NEXT;
using IOB_UT_NEXT.Config;
using MapoSDK;
using Newtonsoft.Json;
using System;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;

namespace IOB_WIN_FILE.IobFile
{
    public class FileEurom63 : IobFile.FileGen
    {
        #region Public Constructors

        /// <summary>
        /// Estende l'init della classe base...
        /// </summary>
        /// <param name="caller">Form chiamante</param>
        /// <param name="IobConfFull">Configurazione (v 4.x)</param>
        public FileEurom63(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
        {
            lgInfo("INIT IobFileEurom63");
            appPath = Path.GetDirectoryName(Application.ExecutablePath);

            string MAX_DELAY_SEC = getOptPar("MAX_DELAY_SEC");
            string CACHE_MULT = getOptPar("CACHE_MULT");
            int.TryParse(MAX_DELAY_SEC, out maxDelaySec);
            int.TryParse(CACHE_MULT, out cacheMult);

#if DEBUG
            maxDelaySec = 60 * 60 * 24;
#endif
        }

        #endregion Public Constructors

        #region Public Methods

        /// <summary>
        /// Effettua vero processing contapezzi
        /// </summary>
        public override void processContapezzi()
        {
            if (utils.CRB("enableContapezzi"))
            {
                try
                {
                    // controllo se sono in sampling della produzione
                    if (actLevel >= Eurom63.ComLevel.ProdRequested)
                    {
                        /************************************************************
                         *
                         * EXAMPLE
                         * DATE, TIME, ActCntCyc, ActTimCyc, ActTimFill, @OutXhour, SetDescJob
                         * 20201007, 21:29:52, 5302, 8.61, 0.50, 10058, Nr. 1000987654.01
                         *
                         * devo prendere il 3° valore
                         *
                         *
                         ************************************************************/

                        // leggo il file della produzione HARD CODED...
                        var sessProd = confE63.ActiveSessions[5];
                        string currPzCount = "";
                        if (sessProd != null)
                        {
                            if (sessProd.Active)
                            {
                                // nome file...
                                string fileName = $"{BaseDir}\\{sessProd.SessionName}.DAT";
                                if (File.Exists(fileName))
                                {
                                    string rawData = "";
                                    using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                                    using (var textReader = new StreamReader(fileStream))
                                    {
                                        rawData = textReader.ReadToEnd();
                                    }
                                    // ora splitto in linee
                                    string[] rawLines = Regex.Split(rawData, "\r\n|\r|\n");
                                    int numRow = rawLines.Length;

                                    // devo avere almeno 2 righe...
                                    if (numRow >= 2)
                                    {
                                        string[] statusData = rawLines[1].Split(',');
                                        currPzCount = statusData[2].Trim();
                                        // salvo se valido
                                        if (!string.IsNullOrEmpty(currPzCount))
                                        {
                                            int newVal = -1;
                                            Int32.TryParse(currPzCount, out newVal);
                                            // verifico SE il contapezzi vada moltiplicato x il
                                            // fattore pzPallet...
                                            if (confE63.PzPallet > 1)
                                            {
                                                newVal = newVal * confE63.PzPallet;
                                            }
                                            // aggiorno contapezzi
                                            contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
                                        }
                                        // ora verifico SE siano validi anche le dataora dei valori
                                        // letti (< 20 sec ritardo da ora...)
                                        string data = statusData[0].Trim();
                                        string ora = statusData[1].Trim();
                                        DateTime adesso = DateTime.Now;
                                        DateTime lastPub = adesso.AddMinutes(-1);
                                        CultureInfo provider = CultureInfo.InvariantCulture;
                                        try
                                        {
                                            lastPub = DateTime.ParseExact($"{data} {ora}", "yyyyMMdd HH:mm:ss", provider);
                                        }
                                        catch
                                        { }
                                        if (Math.Abs(lastPub.Subtract(adesso).TotalSeconds) > maxDelaySec)
                                        {
                                            sessProd.SessionValidUntil = adesso;
                                            // elimino file RSP...
                                            cleanupResp(sessProd.SessionName);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                actLevel = Eurom63.ComLevel.StatusRequested;
                            }
                        }
                        else
                        {
                            actLevel = Eurom63.ComLevel.StatusRequested;
                        }
                    }
                }
                catch (Exception exc)
                {
                    lgError($"Eccezione in processContapezzi:{Environment.NewLine}{exc}");
                }
            }
        }

        /// <summary>
        /// Effettua lettura semafori principale <paramref name="currDispData">Parametri da
        /// aggiornare x display in form</paramref>
        /// </summary>
        public override void readSemafori(ref newDisplayData currDispData)
        {
            base.readSemafori(ref currDispData);
            // in primis controllo status...
            checkCommStatus();

            // init a zero...
            B_input = 0;
            string currStatus = "99999";
            bool readDone = false;
            DateTime adesso = DateTime.Now;
            // leggo il file dela produzione HARD CODED...
            var sessStatus = confE63.ActiveSessions[4];
            // ciclo!
            try
            {
                // controllo se sono in sampling dello stato
                if (actLevel >= Eurom63.ComLevel.StatusRequested)
                {
                    /* -----------------------------------------------------
                     * bitmap MAPO
                     * B0: POWER_ON
                     * B1: RUN
                     * B2: pzCount
                     * B3: allarme
                     * B4: manuale
                     * B5: allarme TCiclo (SLOW)
                     * B6: avvio/spegnimento
                     * B7: Emergenza armata (1= pronto, 0 = emergenza) --> DA INVIARE!!!
                    ----------------------------------------------------- */

                    /******************************************************************
                     *
                     * EXAMPLE file content
                     * DATE, TIME, ActStsMach
                     * 20201007, 21:28:10, 0A000
                     *
                     * Configurazione array status: 5 char status decoding
                     *
                     * Pos1: (status)
                     * 0: poweron
                     * 1: poweroff
                     *
                     * Pos2: (mode)
                     * A: AUTO
                     * S: SEMI auto
                     * M: Manual
                     * U: Setup
                     * H: Hord
                     * C: Maintenance
                     * 0: Unknown
                     * I: Idle
                     *
                     * Pos3: (assist call)
                     * 0: No assistance
                     * 2: Assistance required
                     *
                     * Pos4: (Bad part)
                     * 0: last cycle not bad
                     * 1: last cycle bad
                     *
                     * Pos5: Active Alarm
                     * 0: No alarm
                     * 1: Alarm
                     *
                     *
                     *
                     *******************************************************************/

                    if (sessStatus != null)
                    {
                        if (sessStatus.Active)
                        {
                            // nome file...
                            string fileName = $"{BaseDir}\\{sessStatus.SessionName}.DAT";
                            if (File.Exists(fileName))
                            {
                                string rawData = "";
                                using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                                using (var textReader = new StreamReader(fileStream))
                                {
                                    rawData = textReader.ReadToEnd();
                                }
                                if (!string.IsNullOrEmpty(rawData))
                                {
                                    // ora splitto in linee
                                    string[] rawLines = Regex.Split(rawData, "\r\n|\r|\n");
                                    int numRow = rawLines.Length;

                                    // devo avere almeno 2 righe...
                                    if (numRow >= 2)
                                    {
                                        string[] statusData = rawLines[1].Split(',');
                                        if (statusData.Length >= 3)
                                        {
                                            currStatus = statusData[2].Trim();
                                            if (!string.IsNullOrEmpty(currStatus))
                                            {
                                                currDispData.semIn = Semaforo.SV;
                                            }
                                            // salvo in cache!
                                            Last_CurrStatus.Value = currStatus;
                                            Last_CurrStatus.ValidUntil = DateTime.Now.AddSeconds(maxDelaySec * cacheMult);

                                            // ora verifico SE siano validi anche le dataora dei
                                            // valori letti (< 20 sec ritardo da ora...)
                                            string data = statusData[0].Trim();
                                            string ora = statusData[1].Trim();
                                            DateTime lastPub = adesso.AddMinutes(-1);
                                            CultureInfo provider = CultureInfo.InvariantCulture;
                                            try
                                            {
                                                lastPub = DateTime.ParseExact($"{data} {ora}", "yyyyMMdd HH:mm:ss", provider);
                                            }
                                            catch
                                            { }
                                            if (Math.Abs(lastPub.Subtract(adesso).TotalSeconds) > maxDelaySec)
                                            {
                                                sessStatus.SessionValidUntil = adesso;
                                                // elimino file RSP...
                                                cleanupResp(sessStatus.SessionName);
                                            }
                                            readDone = true;
                                        }
                                        else
                                        {
                                            lgError($"Decodifica StatusData in errore: trovati {statusData.Length} campi in {rawLines}");
                                            // se valido RILEGGO ultimo curr status
                                            if (Last_CurrStatus.ValidUntil > adesso)
                                            {
                                                currStatus = Last_CurrStatus.Value;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        lgError($"Lettura file stato in errore: trovate {numRow} linee");
                                    }
                                }
                                // se valido RILEGGO ultimo curr status
                                if (Last_CurrStatus.ValidUntil > adesso)
                                {
                                    currStatus = Last_CurrStatus.Value;
                                }
                            }
                            else
                            {
                                // se valido RILEGGO ultimo curr status
                                if (Last_CurrStatus.ValidUntil > adesso)
                                {
                                    currStatus = Last_CurrStatus.Value;
                                }
                                // abbasso status...
                                actLevel--;
                            }
                        }
                        else
                        {
                            // se valido RILEGGO ultimo curr status
                            if (Last_CurrStatus.ValidUntil > adesso)
                            {
                                currStatus = Last_CurrStatus.Value;
                            }
                        }
                    }
                    else
                    {
                        // se valido RILEGGO ultimo curr status
                        if (Last_CurrStatus.ValidUntil > adesso)
                        {
                            currStatus = Last_CurrStatus.Value;
                        }
                    }

                    // processo il currentStatus... parto da poweron
                    B_input = currStatus[0] == '1' ? 0 : 1;

                    // aggiungo il bit NON emergenza
                    B_input += (1 << 7);

                    // ora MODE
                    switch (currStatus[1])
                    {
                        case 'A':
                            B_input += (1 << 1);
                            break;

                        case 'S':
                        case 'M':
                        case 'U':
                            B_input += (1 << 4);
                            break;

                        default:
                            // loggo cosa trovo (CREDO
                            break;
                    }
                    // ora cerco allarmi
                    if (currStatus[4] == '1')
                    {
                        B_input += (1 << 3);
                    }
                    // controllo se diverso faccio log!
                    if (!B_input.Equals(Last_B_Input.Value))
                    {
                        lgInfo($"B_Input variato: {Last_B_Input.Value} --> {B_input} | currStatus: {currStatus}");
                    }
                    // salvo B_Input in cache!
                    Last_B_Input.Value = B_input;
                    Last_B_Input.ValidUntil = DateTime.Now.AddSeconds(maxDelaySec * cacheMult);
                }
                // se disponibile riporto B_Input precedente
                else
                {
                    if (Last_B_Input.ValidUntil > adesso)
                    {
                        B_input = Last_B_Input.Value;
                    }
                }
                // riporto bitmap...
                reportRawInput(ref currDispData);
            }
            catch (Exception exc)
            {
                lgError(exc, "Errore in readSemafori x IOB FILE");
                if (currDispData != null)
                    currDispData.semIn = Semaforo.SR;
            }

            // ultimo controllo...
            if (!readDone)
            {
                // se valido RILEGGO ultimo B_INPUT
                if (Last_B_Input.ValidUntil > adesso)
                {
                    B_input = Last_B_Input.Value;
                }
            }
        }

        /// <summary>
        /// Effettua reset del contapezzi, NON PERMESSO per EM63 (read only)
        /// </summary>
        /// <returns></returns>
        public override bool resetContapezziPLC(string codTav)
        {
            bool answ = false;
            return answ;
        }

        /// <summary>
        /// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON PERMESSO per EM63 (read only)
        /// </summary>
        /// <returns></returns>
        public override bool setcontapezziPLC(int newPzCount, string codTav)
        {
            bool answ = false;
            return answ;
        }

        /// <summary>
        /// Connessione
        /// </summary>
        public override void tryConnect()
        {
            var nextLevel = Eurom63.ComLevel.IsConnected;
            var connectSession = confE63.ActiveSessions[0];
            processSession(nextLevel, ref connectSession);
            queueInEnabCurr = true;
        }

        /// <summary>
        /// Disconnessione
        /// </summary>
        public override void tryDisconnect()
        {
            connectionOk = false;
            queueInEnabCurr = false;
            try
            {
                abortPrevJob();
                cleanupFolder();
            }
            catch (Exception exc)
            {
                lgError(exc, "Eccezione in tryDisconnect");
            }
        }

        #endregion Public Methods

        #region Internal Methods

        /// <summary>
        /// Metodi preliminari x comunicazione:
        /// - richiesta connessione
        /// - richiesta stato attivo
        /// </summary>
        internal void checkCommStatus()
        {
            // init obj display
            newDisplayData currDispData = new newDisplayData();
            currDispData.semIn = Semaforo.ND;

            switch (actLevel)
            {
                case Eurom63.ComLevel.None:
                    tryConnect();
                    currDispData.semIn = Semaforo.SS;
                    break;

                case Eurom63.ComLevel.IsConnected:
                    requestInfo();
                    currDispData.semIn = Semaforo.SS;
                    break;

                case Eurom63.ComLevel.HasInfo:
                    setMachineTime();
                    currDispData.semIn = Semaforo.SS;
                    break;

                case Eurom63.ComLevel.TimeSet:
                    abortPrevJob();
                    currDispData.semIn = Semaforo.SG;
                    break;

                case Eurom63.ComLevel.ChannelOk:
                    requestStatusData();
                    currDispData.semIn = Semaforo.SG;
                    break;

                case Eurom63.ComLevel.StatusRequested:
                    requestProdData();
                    currDispData.semIn = Semaforo.SV;
                    break;

                case Eurom63.ComLevel.ProdRequested:
                    checkSampling();
                    currDispData.semIn = Semaforo.SV;
                    break;

                default:
                    break;
            }
            if (utils.CRB("verbose"))
            {
                lgInfo($"DONE checkCommStatus | actLevel {actLevel}");
            }
            raiseRefresh(currDispData);
        }

        /// <summary>
        /// Pulizia preliminare folder comunicazione
        /// </summary>
        internal override void cleanupFolder()
        {
            // elimino OGNI file per tipo configurato
            foreach (var cleanExt in confE63.cleanupExt)
            {
                string[] file2del = Directory.GetFiles(BaseDir, cleanExt);
                foreach (var file in file2del)
                {
                    try
                    {
                        File.Delete(file);
                    }
                    catch
                    { }
                }
            }
        }

        /// <summary>
        /// Pulizia folder dai file RSP della sessione
        /// </summary>
        internal void cleanupResp(string sessionName)
        {
            string[] file2del = Directory.GetFiles(BaseDir, $"{sessionName}.RSP");
            foreach (var file in file2del)
            {
                try
                {
                    File.Delete(file);
                }
                catch
                { }
            }
        }

        /// <summary>
        /// Ricarica conf adapter...
        /// </summary>
        internal override void reloadAdapterConf()
        {
            // init obj display
            newDisplayData currDispData = new newDisplayData();
            lgInfo("BEGIN reloadAdapterConf");
            // inizializzo LUT decodifica
            string jsonConf = getOptPar("LUT_CONF");
            if (!string.IsNullOrEmpty(jsonConf))
            {
                string jsonFullPath = $"{Application.StartupPath}/DATA/CONF/{jsonConf}";
                lgInfo($"Apertura file {jsonFullPath}");
                StreamReader reader = new StreamReader(jsonFullPath);
                string jsonData = reader.ReadToEnd();
                if (!string.IsNullOrEmpty(jsonData))
                {
                    try
                    {
                        confE63 = JsonConvert.DeserializeObject<Eurom63.ProtoConf>(jsonData);

                        // salvo baseUri
                        BaseDir = confE63.BaseDir;
                        lgInfo($"baseDir = {BaseDir}");
                        // imposto a zero la bitmap x riavvio!
                        B_input = 0;
                        // FORZO invio dati...
                        accodaSigIN(ref currDispData);
                        // loggo!
                        lgInfo($"init input bitmap to zero: {B_input}");
                    }
                    catch (Exception exc)
                    {
                        lgError(exc, "Eccezione in decodifica conf json");
                    }
                }
                reader.Dispose();
            }
            lgInfo("DONE reloadAdapterConf");
            raiseRefresh(currDispData);
        }

        #endregion Internal Methods

        #region Protected Fields

        protected static DateTime lastStatusDecr = DateTime.Now;

        /// <summary>
        /// step di comunicazione attivo
        /// </summary>
        protected Eurom63.ComLevel actLevel = Eurom63.ComLevel.None;

        /// <summary>
        /// DIrectory eseguibile corrente
        /// </summary>
        protected string appPath = Directory.GetCurrentDirectory();

        /// <summary>
        /// Moltiplicatore durata cache
        /// </summary>
        protected int cacheMult = 4;

        /// <summary>
        /// Oggetti decodificati da pagina
        /// </summary>
        protected Eurom63.ProtoConf confE63;

        /// <summary>
        /// Valore currStatus validato (per gestione "disconnessioni")
        /// </summary>
        protected CachedInt Last_B_Input = new CachedInt() { Value = 0 };

        /// <summary>
        /// Valore currStatus validato (per gestione "disconnessioni")
        /// </summary>
        protected CachedString Last_CurrStatus = new CachedString() { Value = "00000" };

        /// <summary>
        /// Massimo delay lettura dati prima di considerarli scaduti (30 sec, ma x test 1 gg)
        /// </summary>
        protected int maxDelaySec = 30;

        #endregion Protected Fields

        #region Protected Methods

        protected void abortPrevJob()
        {
            var nextLevel = Eurom63.ComLevel.ChannelOk;
            var connectSession = confE63.ActiveSessions[3];
            processSession(nextLevel, ref connectSession);
#if false
            // qui per sicurezza PULISCE TUTTO
            cleanupFolder();
#endif
        }

        /// <summary>
        /// Verifica una sessione configurata (ovvero la comunicazione su TUTTI i file associati)
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        protected bool checkRequest(Eurom63.Session session)
        {
            bool answ = false;
            string fileName = "";
            if (session != null)
            {
                fileName = $"{BaseDir}\\{session.SessionName}.REQ";
                answ = File.Exists(fileName);
            }
            return answ;
        }

        /// <summary>
        /// Verifica se ci sia una risposta POSITIVA
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        protected bool checkResp(Eurom63.Session session)
        {
            bool answ = false;
            string fileName = "";
            if (session != null)
            {
                fileName = $"{BaseDir}\\{session.SessionName}.RSP";
                if (File.Exists(fileName))
                {
                    // verifico contenuto
                    //string rawData = File.ReadAllText(fileName);
                    string rawData = "";
                    // lettura in modo NON esclusivo...
                    using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                    using (var textReader = new StreamReader(fileStream))
                    {
                        rawData = textReader.ReadToEnd();
                    }
                    // se la stringa session.RespOk contiene | significa ci sono + valori ammessi e
                    // li controlla tutti
                    if (session.RespOk.Contains("|"))
                    {
                        // splitto e testo tutti...
                        string[] validRespOk = session.RespOk.Split('|');
                        foreach (var item in validRespOk)
                        {
                            answ = answ || rawData.Contains(item);
                        }
                    }
                    else
                    {
                        answ = rawData.Contains(session.RespOk);
                    }
                }
            }
            return answ;
        }

        /// <summary>
        /// verifica periodica dei campionamenti:
        /// - i due jobs devono essere attivi e non scaduti
        /// - non devo aver già resettato...
        /// </summary>
        protected void checkSampling()
        {
            var currSession = confE63.ActiveSessions[4];
            checkSessionActive(currSession, Eurom63.ComLevel.ChannelOk);

            currSession = confE63.ActiveSessions[5];
            checkSessionActive(currSession, Eurom63.ComLevel.ChannelOk);
        }

        /// <summary>
        /// Elimina i file della sessione indicata (SE è un task ciclico --&gt; solo RSP)
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        protected bool cleanupSession(Eurom63.Session session)
        {
            bool answ = false;
            if (session != null)
            {
                // solo se scaduta validità...
                if (session.SessionValidUntil < DateTime.Now)
                {
                    string searchPattern = $"{session.SessionName}.*";
                    // task ciclico?
                    if (session.Cycle)
                    {
                        // solo risposta!
                        searchPattern = $"{session.SessionName}.RSP";
                    }
                    string[] file2del = Directory.GetFiles(BaseDir, searchPattern);
                    foreach (var file in file2del)
                    {
                        try
                        {
                            File.Delete(file);
                        }
                        catch
                        { }
                    }
                }
            }
            return answ;
        }

        /// <summary>
        /// Processa una sessione
        /// - andando a verificare l'esistenza della REQ + se esito positivo pulizia
        /// - andando a richeidere di nuovo risposta
        /// </summary>
        /// <param name="nextLevel"></param>
        /// <param name="connectSession"></param>
        protected void processSession(Eurom63.ComLevel nextLevel, ref Eurom63.Session connectSession)
        {
            if (connectSession != null)
            {
                // controllo esistenza directory --> segno connected...
                connectionOk = Directory.Exists(BaseDir);
                DateTime adesso = DateTime.Now;
                if (connectionOk)
                {
                    // verifico se ci sia risp CONNECT
                    if (checkResp(connectSession))
                    {
                        // aggiorno livello
                        actLevel = nextLevel;
                        parentForm.displayTaskAndLog($"Adp Level: {nextLevel}");
                        // elimino file sessione
                        cleanupSession(connectSession);
                        connectSession.Active = connectSession.Cycle;
                        connectSession.Passed = true;
                    }
                    // verifico SE ci sia la richiesta sennò la chiedo...
                    else if (!checkRequest(connectSession))
                    {
                        copyRequestFiles(connectSession, adesso);
                    }
                    // richiedo SE non ci fosse i dati CONNECT...
                    else
                    {
                        // evito di richiedere SE non fosse già scaduta richiesta...
                        if (adesso > connectSession.RetryVeto || !checkRequest(connectSession))
                        {
                            // pulisco eventuali risp vecchie
                            cleanupResp(connectSession.SessionName);
                            copyRequestFiles(connectSession, adesso);
                        }
                    }
                }
                else
                {
                    // aspetto prima di riprovare...
                    Thread.Sleep(50);
                }
            }
            else
            {
                // aspetto prima di riprovare...
                Thread.Sleep(50);
            }
        }

        /// <summary>
        /// Processa i file della sessione indicata (copy + transform)
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        protected bool processSessionFile(Eurom63.Session session)
        {
            bool answ = false;
            if (session != null)
            {
                string fileFrom = "";
                string fileTo = "";
                // processo OGNI file sessione x farne copia
                foreach (var file2Proc in session.FileList)
                {
                    fileFrom = $"{appPath}\\{file2Proc.Path}";
                    fileTo = $"{BaseDir}\\{Path.GetFileName(file2Proc.Path)}";
                    string rawData = File.ReadAllText(fileFrom);

                    if (file2Proc.OprReq == Eurom63.FileOpr.Copy)
                    {
                        // scrivo!
                        File.WriteAllText(fileTo, rawData);
                    }
                    else
                    {
                        // leggo file originale... e processo sostituzioni
                        rawData = rawData.Replace("{DTNow}", DateTime.Now.ToString("HHmmssyyyyMMdd"));
                        // ora in posizione definitiva
                        File.WriteAllText(fileTo, rawData);
                    }
                }
            }
            return answ;
        }

        /// <summary>
        /// Effettua richiesta info x macchina (validare startup process)
        /// </summary>
        protected void requestInfo()
        {
            var nextLevel = Eurom63.ComLevel.HasInfo;
            var connectSession = confE63.ActiveSessions[1];
            processSession(nextLevel, ref connectSession);
        }

        protected void requestProdData()
        {
            var nextLevel = Eurom63.ComLevel.ProdRequested;
            var connectSession = confE63.ActiveSessions[5];
            processSession(nextLevel, ref connectSession);
        }

        protected void requestStatusData()
        {
            var nextLevel = Eurom63.ComLevel.StatusRequested;
            var connectSession = confE63.ActiveSessions[4];
            processSession(nextLevel, ref connectSession);
        }

        protected void setMachineTime()
        {
            var nextLevel = Eurom63.ComLevel.TimeSet;
            var connectSession = confE63.ActiveSessions[2];
            processSession(nextLevel, ref connectSession);
        }

        #endregion Protected Methods

        #region Private Methods

        /// <summary>
        /// Se la sessione fosse scaduta o non attiva --&gt; torna al livello indicato
        /// </summary>
        /// <param name="currSession"></param>
        private void checkSessionActive(Eurom63.Session currSession, Eurom63.ComLevel nextLevel)
        {
            // SOLO SE ha senso che controllo (sono in sampling...)
            if (actLevel > Eurom63.ComLevel.HasInfo)
            {
                DateTime adesso = DateTime.Now;
                // devono essere ATTIVE le sessioni di campionamento... e NON scadute
                if (!currSession.Active || currSession.SessionValidUntil < adesso)
                {
                    // controllo ultimo downgrade status
                    if (lastStatusDecr.AddSeconds(3) < adesso)
                    {
                        // elimino TUTTE le risposte...
                        cleanupResp(currSession.SessionName);
                        // registro downgrade status...
                        lastStatusDecr = adesso;
                        // imposto livellotornando indietro di 1 alla volta... senza andare in negativoS
                        actLevel = actLevel - 1;
                        actLevel = actLevel > 0 ? actLevel : 0;
                        lgInfo($"Sessione inattiva, {actLevel + 1} --> {actLevel}");
                    }
                }
            }
        }

        /// <summary>
        /// Effettua copia file richeiste + update timing
        /// </summary>
        /// <param name="connectSession"></param>
        /// <param name="adesso"></param>
        private void copyRequestFiles(Eurom63.Session connectSession, DateTime adesso)
        {
            // processo richiesta
            processSessionFile(connectSession);
            if (adesso > connectSession.SessionValidUntil)
            {
                connectSession.Active = !connectSession.Cycle;
            }
            connectSession.Passed = false;
            connectSession.SessionStarted = adesso;
            connectSession.SessionValidUntil = adesso.AddMinutes(connectSession.ValidityMinutes);
            connectSession.RetryVeto = adesso.AddSeconds(connectSession.RetrySec);
        }

        #endregion Private Methods
    }
}