using IOB_UT_NEXT.Config;
using IOB_UT_NEXT.Config.Base;
using IOB_UT_NEXT.Config.Special;
using MapoSDK;
using Newtonsoft.Json;
using NLog;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management.Instrumentation;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Threading;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using static IOB_UT_NEXT.BaseAlarmConf;
using static IOB_UT_NEXT.Config.EnumConf;
using static System.Net.Mime.MediaTypeNames;

// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace IOB_UT_NEXT.Config
{
    /// <summary>
    /// Albero configurazione globale IOB in formato serializable
    /// </summary>
    [Serializable]
    public class IobConfTree
    {
        /// <summary>
        /// Init classe configurazione
        /// </summary>
        public IobConfTree()
        {
            Log = LogManager.GetCurrentClassLogger();
        }

        /// <summary>
        /// Init classe configurazione da file
        /// </summary>
        public IobConfTree(string confFilePath)
        {
            Log = LogManager.GetCurrentClassLogger();
            if (File.Exists(confFilePath))
            {
                IobConfTree newConfObj = new IobConfTree();
                // verifico TIPO file...
                string fileExt = Path.GetExtension(confFilePath);
                string fileName = Path.GetFileName(confFilePath);
                string rawData = File.ReadAllText(confFilePath);
                if (!string.IsNullOrEmpty(rawData))
                {
                    // leggo in base al tipo...
                    switch (fileExt)
                    {
                        case "yaml":
                        case "yml":
                            var deserializer = new DeserializerBuilder()
                                                .WithNamingConvention(CamelCaseNamingConvention.Instance)
                                                .Build();
                            try
                            {
                                newConfObj = deserializer.Deserialize<IobConfTree>(rawData);
                            }
                            catch (Exception exc)
                            {
                                Log.Error($"Eccezione in LoadFromYaml{Environment.NewLine}{exc}");
                            }
                            break;
                        default:
                            break;
                    }
                    if (newConfObj != null)
                    {
                        // ora copio in oggetto corrente...
                        General = newConfObj.General;
                        Device = newConfObj.Device;
                        SignalProc = newConfObj.SignalProc;
                        OptPar = newConfObj.OptPar;
                        MapoMes = newConfObj.MapoMes;
                        Special = newConfObj.Special;
                        TCDataConf = newConfObj.TCDataConf;
                        Actions = newConfObj.Actions;
                        // sovrascrivo filename
                        General.FileName = fileName;
                    }
                }
            }
        }

        /// <summary>
        /// Restituisce un oggetto di conf leggendo INI ed effettuando conversione
        /// </summary>
        /// <param name="iniFilePath"></param>
        /// <returns></returns>
        public static IobConfTree LoadFromINI(string iniFilePath)
        {
            IobConfTree newConfObj = new IobConfTree();
            try
            {
                // leggo file INI
                IniFile fIni = new IniFile(iniFilePath);
                string dirPath = Path.GetDirectoryName(iniFilePath);
                string codIob = Path.GetFileNameWithoutExtension(iniFilePath);
                // elenco valori OPT_Par già acquisiti da togliere dal dizionario generico in coda 
                List<string> exclOptPar = new List<string>();

                // Dati generali (vendor, modello...)
                int maxErrCheck = 50;
                if (!string.IsNullOrEmpty(utils.CRS("maxErroriCheck")))
                {
                    maxErrCheck = utils.CRI("maxErroriCheck");
                }
                Assembly entryAssembly = Assembly.GetEntryAssembly();
                string relVers = entryAssembly != null ? $"{entryAssembly?.GetName().Version}" : $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Version}";
                newConfObj.General = new IobDto()
                {
                    CodIOB = fIni.ReadString("IOB", "IOB_NAME", codIob),
                    CodGruppoIob = fIni.ReadString("OPTPAR", "CodGruppoIob", "ND-00"),
                    DelayReadPzCount = Convert.ToDouble(fIni.ReadString("OPTPAR", "DELAY_READ_PZ_COUNT", "0")),
                    DelayReadPzCountSetup = Convert.ToDouble(fIni.ReadString("OPTPAR", "DELAY_READ_PZ_COUNT_SETUP", "0.01")),
                    FileName = Path.GetFileName(iniFilePath),
                    Customer = fIni.ReadString("TAGS", "Customer", "EgalWare"),
                    EnabelPodlManFull = bool.Parse(fIni.ReadString("OPTPAR", "EnabelPodlManFull", "false")),
                    EnabRedisQue = bool.Parse(fIni.ReadString("IOB", "EnableRedisQueue", "true")),
                    MachWLoopSingleThread = bool.Parse(fIni.ReadString("OPTPAR", "MachSingleThread", "false")),
                    MaxErroriCheck = fIni.ReadInteger("OPTPAR", "MAX_ERR_CHECK", maxErrCheck),
                    MaxPingRetry = fIni.ReadInteger("OPTPAR", "MAX_PING_RETRY", 5),
                    MinDeltaSec = fIni.ReadInteger("IOB", "MinDeltaSec", 6),
                    RelVers = relVers,// $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Version}",
                    ResetAlarmOnStart = bool.Parse(fIni.ReadString("OPTPAR", "SEND_ALARM_RESET", "false")),
                    SenMachineConf = bool.Parse(fIni.ReadString("OPTPAR", "SEND_MACHINE_CONF", "true")),
                    WaitRecMsec = Convert.ToInt32(fIni.ReadString("OPTPAR", "WAIT_REC_MSEC", "90000")),
                    WatchDogPeriod = Convert.ToInt32(fIni.ReadString("OPTPAR", "WATCHDOG_PERIOD", "2")),
                    DisabWDST = bool.Parse(fIni.ReadString("OPTPAR", "DISABLE_SEND_WDST", "true"))
                };
                // escludo valori OPTPAR gestiti
                exclOptPar.Add("CodGruppoIob");
                exclOptPar.Add("DELAY_READ_PZ_COUNT");
                exclOptPar.Add("DISABLE_SEND_WDST");
                exclOptPar.Add("EnabelPodlManFull");
                exclOptPar.Add("MachSingleThread");
                exclOptPar.Add("MAX_ERR_CHECK");
                exclOptPar.Add("MAX_PING_RETRY");
                exclOptPar.Add("SEND_ALARM_RESET");
                exclOptPar.Add("SEND_MACHINE_CONF");
                exclOptPar.Add("WAIT_REC_MSEC");
                exclOptPar.Add("WATCHDOG_PERIOD");

                // da togliere da conf app...
                int uiTimer = utils.CRI("timerIntMs");
                string rawTimer = fIni.ReadString("OPTPAR", "timerIntMs", "");
                if (!string.IsNullOrEmpty(rawTimer))
                {
                    int.TryParse(rawTimer, out uiTimer);
                }
                exclOptPar.Add("timerIntMs");
                // ultimo controllo: timer deve essere compreso tra 10 a 500 x evitare task troppo veloci o lenti...
                uiTimer = uiTimer < 10 ? 10 : uiTimer;
                uiTimer = uiTimer > 500 ? 500 : uiTimer;
                // init struttura coi valori calcolati da conf...
                newConfObj.General.Timers = new TimersDto()
                {
                    MsUI = uiTimer,
                    MsVHF = uiTimer * 6 < 6000 ? uiTimer * 6 : 6000,
                    MsHF = uiTimer * utils.CRI("fastCount") < 15000 ? uiTimer * utils.CRI("fastCount") : 15000,
                    MsMF = uiTimer * utils.CRI("normCount") < 45000 ? uiTimer * utils.CRI("normCount") : 45000,
                    MsLF = uiTimer * utils.CRI("slowCount") < 90000 ? uiTimer * utils.CRI("slowCount") : 90000,
                    MsVLF = uiTimer * utils.CRI("verySlowCount") < 300000 ? uiTimer * utils.CRI("verySlowCount") : 300000,
                    MsSample = uiTimer * utils.CRI("sampleMemCount") < 600000 ? uiTimer * utils.CRI("sampleMemCount") : 600000,
                };


                // tipo adapter// verifico tipo adapter
                try
                {
                    newConfObj.General.IobType = (tipoAdapter)Enum.Parse(typeof(tipoAdapter), fIni.ReadString("IOB", "CNCTYPE", "ND"));
                }
                catch (Exception exc)
                {
                    newConfObj.General.IobType = tipoAdapter.ND;
                    string rawVal = fIni.ReadString("IOB", "CNCTYPE", "DEMO");
                    newConfObj.lgError($"Eccezione in conversione tipo adapter: richiesto {rawVal} | tipo non codificato...{Environment.NewLine}{exc}");
                }

                // Gestione conf ODL
                newConfObj.Odl = new OdlDto()
                {
                    AutoChangeOdl = bool.Parse(fIni.ReadString("OPTPAR", "AUTO_CHANGE_ODL", "false")),
                    ChangeOdlMode = fIni.ReadString("OPTPAR", "CHANGE_ODL_MODE", ""),
                    DisableOdl = bool.Parse(fIni.ReadString("OPTPAR", "DISABLE_ODL", "false")),
                    OdlDurationHours = fIni.ReadInteger("OPTPAR", "CHANGE_ODL_HOURS", 24),
                    IdleStateMin = fIni.ReadInteger("OPTPAR", "CHANGE_ODL_IDLE_MIN", 0),
                    // il change ODL al reset contapezzi può essere configurato in 2 modi: con chiave esplicita oppure con il CHANGE_ODL_MODE specifico...
                    ChangeOnResetCount = bool.Parse(fIni.ReadString("OPTPAR", "CHANGE_PZCOUNT_RESET", "false")) || (fIni.ReadString("OPTPAR", "CHANGE_ODL_MODE", "") == "PZCOUNT_RESET"),
                    // gestione precedente: bastava 1 solo pezzo pre...
                    ResetCountMinPre = fIni.ReadInteger("OPTPAR", "RESET_COUNT_PRE", 1),
                    // e reset a zero...
                    ResetCountMaxPost = fIni.ReadInteger("OPTPAR", "RESET_COUNT_POST", 0),
                    VetoCheckOdlSec = fIni.ReadInteger("OPTPAR", "VETO_CHECK_ODL_SEC", 10)
                };
                exclOptPar.Add("AUTO_CHANGE_ODL");
                exclOptPar.Add("CHANGE_ODL_MODE");
                exclOptPar.Add("CHANGE_ODL_HOURS");
                exclOptPar.Add("CHANGE_ODL_IDLE_MIN");
                exclOptPar.Add("CHANGE_PZCOUNT_RESET");
                exclOptPar.Add("DISABLE_ODL");
                exclOptPar.Add("RESET_COUNT_PRE");
                exclOptPar.Add("RESET_COUNT_POST");
                exclOptPar.Add("VETO_CHECK_ODL_SEC");

                // gestione FluxLog
                float globDBand = 1;
                string sDBand = fIni.ReadString("OPTPAR", "GLOBAL_DBAND", "");
                if (!string.IsNullOrEmpty(sDBand))
                {
                    float.TryParse(sDBand, out globDBand);
                }
                newConfObj.FluxLog = new FluxLogDto()
                {
                    AutoSnapshotDossier = bool.Parse(fIni.ReadString("OPTPAR", "AUTO_SNAPSHOT_DOSSIER", "false")),
                    CopyDyn2MemWrite = bool.Parse(fIni.ReadString("OPTPAR", "COPY_DYN2PROD_DATA", "false")),
                    DemFactDynData = fIni.ReadInteger("OPTPAR", "DEM_FACT_DYN_DATA", 1),
                    DisDynData = bool.Parse(fIni.ReadString("OPTPAR", "DISABLE_DYN_DATA", "false")),
                    DisDynDataRangeCheck = bool.Parse(fIni.ReadString("OPTPAR", "disDynDataRangeCheck", "false")),
                    EnableDynData = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_DYN_DATA", "true")),
                    EnableFilt = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_DATA_FILTER", "false")),
                    EnableOverrides = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_OVERRIDES", "false")),
                    EnableSlowData = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_SLOW_DATA", "false")),
                    ForceDynData = bool.Parse(fIni.ReadString("OPTPAR", "FORCE_DYN_DATA", "false")),
                    GlobalDeadBand = globDBand,
                    MaxSecReload = fIni.ReadInteger("OPTPAR", "MAX_ELAPSED_TIME_SEC", 1800),
                    SamplePeriod = fIni.ReadInteger("OPTPAR", "BASE_SAMPLE_PERIOD", 3600),
                    SendDynDataRec = bool.Parse(fIni.ReadString("OPTPAR", "SEND_DYN_DATA_FLOG", "false")),
                    VetoSendSnapshotSec = fIni.ReadInteger("OPTPAR", "VETO_SEND_SNAPSHOT", 6)
                };
                exclOptPar.Add("AUTO_SNAPSHOT_DOSSIER");
                exclOptPar.Add("COPY_DYN2PROD_DATA");
                exclOptPar.Add("BASE_SAMPLE_PERIOD");
                exclOptPar.Add("DEM_FACT_DYN_DATA");
                exclOptPar.Add("DISABLE_DYN_DATA");
                exclOptPar.Add("disDynDataRangeCheck");
                exclOptPar.Add("ENABLE_DATA_FILTER");
                exclOptPar.Add("ENABLE_OVERRIDES");
                exclOptPar.Add("ENABLE_DYN_DATA");
                exclOptPar.Add("ENABLE_SLOW_DATA");
                exclOptPar.Add("FORCE_DYN_DATA");
                exclOptPar.Add("GLOBAL_DBAND");
                exclOptPar.Add("MAX_ELAPSED_TIME_SEC");
                exclOptPar.Add("SEND_DYN_DATA_FLOG");
                exclOptPar.Add("VETO_SEND_SNAPSHOT");

                newConfObj.Device = new DeviceDto()
                {
                    DisabExeTask = bool.Parse(fIni.ReadString("IOB", "DIS_EXE_TASK", "false")),
                    DisabPing = bool.Parse(fIni.ReadString("OPTPAR", "NO_PING", "false")),
                    DisabStateCh = bool.Parse(fIni.ReadString("IOB", "DIS_STATE_CH", "false")),
                    DisabSigIn = bool.Parse(fIni.ReadString("OPTPAR", "VETO_SIG_IN", "false")),
                    DisabResendScaduto = bool.Parse(fIni.ReadString("OPTPAR", "VETO_SCADUTO", "false")),
                    EnabMemRewrite = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_MEM_REWRITE", "true")),
                    EnabProgName = bool.Parse(fIni.ReadString("CNC", "GETPRGNAME", "true")),
                    EnabPzCount = utils.CRB("enableContapezzi"),
                    ExeModeCheckBypass = bool.Parse(fIni.ReadString("OPTPAR", "EXEMODE_CHECK_BYPASS", "false")),
                    Model = fIni.ReadString("MACHINE", "MODEL", "ND"),
                    MinRespTimeMs = fIni.ReadInteger("OPTPAR", "MIN_RESP_TIME_MS", 5),
                    MinRefreshPeriodSec = fIni.ReadInteger("OPTPAR", "MIN_REF_PERIOD_SEC", 15),
                    MultiIobListRaw = fIni.ReadString("OPTPAR", "IOB_MULTI_CNAME", ""),
                    NumArtMode = fIni.ReadString("OPTPAR", "SET_NUM_ART", ""),
                    NumOdlMode = fIni.ReadString("OPTPAR", "SET_NUM_COM", ""),
                    PoweroffTimeOutSec = fIni.ReadInteger("OPTPAR", "POWEROFF_TIMEOUT_SEC", 120),
                    PzCadMode = fIni.ReadString("OPTPAR", "PZCAD_MODE", ""),
                    PzCountDelay = utils.CRI("pzCountDelay"),
                    PzCountMode = fIni.ReadString("OPTPAR", "PZCOUNT_MODE", ""),
                    PzReqMode = fIni.ReadString("OPTPAR", "PZREQ_MODE", ""),
                    PzTotMode = fIni.ReadString("OPTPAR", "PZGTOT_MODE", ""),
                    ReadErrorMax = fIni.ReadInteger("OPTPAR", "READ_ERROR_MAX", 20),
                    ReadErrorSleepTime = fIni.ReadInteger("OPTPAR", "READ_ERROR_SLEEP_TIME", 20000),
                    StartupVetoQueueIN = fIni.ReadInteger("OPTPAR", "VETO_QUEUE_IN", 6),
                    Vendor = fIni.ReadString("MACHINE", "VENDOR", "STEAMWARE"),
                    Connect = new ConnectionDto()
                    {
                        IpAddr = fIni.ReadString("CNC", "IP", "::1"),
                        PingIpAddr = fIni.ReadString("CNC", "PING_IP", fIni.ReadString("CNC", "IP", "::1")),
                        PingMsTimeout = fIni.ReadInteger("IOB", "PING_MS_TIMEOUT", 500),
                        Port = fIni.ReadString("CNC", "PORT", "0"),
                        MaxTryPing = fIni.ReadInteger("OPTPAR", "MAX_TRY_PING", 4),
                        EnableRestart = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_CLI_RESTART", "false"))
                    }
                };
                exclOptPar.Add("ENABLE_CLI_RESTART");
                exclOptPar.Add("ENABLE_MEM_REWRITE");
                exclOptPar.Add("EXEMODE_CHECK_BYPASS");
                exclOptPar.Add("IOB_MULTI_CNAME");
                exclOptPar.Add("MAX_TRY_PING");
                exclOptPar.Add("MIN_RESP_TIME_MS");
                exclOptPar.Add("MIN_REF_PERIOD_SEC");
                exclOptPar.Add("NO_PING");
                exclOptPar.Add("POWEROFF_TIMEOUT_SEC");
                exclOptPar.Add("PZCAD_MODE");
                exclOptPar.Add("PZCOUNT_MODE");
                exclOptPar.Add("PZREQ_MODE");
                exclOptPar.Add("PZGTOT_MODE");
                exclOptPar.Add("READ_ERROR_MAX");
                exclOptPar.Add("READ_ERROR_SLEEP_TIME");
                exclOptPar.Add("SET_NUM_ART");
                exclOptPar.Add("SET_NUM_COM");
                exclOptPar.Add("VETO_QUEUE_IN");
                exclOptPar.Add("VETO_SIG_IN");
                exclOptPar.Add("VETO_SCADUTO");

                // multi...
                if (!string.IsNullOrEmpty(fIni.ReadString("OPTPAR", "IS_MULTI", "false")))
                {
                    string rawVal = fIni.ReadString("OPTPAR", "IS_MULTI", "");
                    if (rawVal.Length > 1)
                    {
                        newConfObj.Device.IsMulti = Convert.ToBoolean(fIni.ReadString("OPTPAR", "IS_MULTI", "false"));
                    }
                    else
                    {
                        newConfObj.Device.IsMulti = fIni.ReadInteger("OPTPAR", "IS_MULTI", 0) == 1;
                    }
                }
                exclOptPar.Add("IS_MULTI");
                // verifico comunque se ci fosse Device.MultiIobListRaw
                if (!string.IsNullOrEmpty(newConfObj.Device.MultiIobListRaw))
                {
                    newConfObj.Device.IsMulti = true;
                    newConfObj.Device.MultiIobList = newConfObj.Device.MultiIobListRaw.Split(',').ToList() ?? new List<string>();

                }

                // BLINK
                newConfObj.SignalProc.BlinkMaxCounter = Convert.ToInt32(fIni.ReadString("BLINK", "MAX_COUNTER_BLINK", "1"));
                newConfObj.SignalProc.BlinkFilterMask = Convert.ToInt32(fIni.ReadString("BLINK", "BLINK_FILT", "0"));
                newConfObj.TCDataConf.MaxDelayFactor = Convert.ToDouble(fIni.ReadString("OPTPAR", "TC_MAX_TC_FACTOR", "1.2").Replace(".", ","));
                newConfObj.TCDataConf.Lambda = Convert.ToDouble(fIni.ReadString("OPTPAR", "TC_LAMBDA", "0.5").Replace(".", ","));
                newConfObj.TCDataConf.MaxIncrPz = Convert.ToDouble(fIni.ReadString("OPTPAR", "TC_MAX_INCR", "5").Replace(".", ","));

                exclOptPar.Add("TC_MAX_TC_FACTOR");
                exclOptPar.Add("TC_LAMBDA");
                exclOptPar.Add("TC_MAX_INCR");

                // Server
                string MpIp = fIni.ReadString("SERVER", "MPIP", "::1");
                if (!string.IsNullOrEmpty(MpIp))
                {
                    newConfObj.MapoMes.Transport = MpIp.StartsWith("https://") ? "https" : "http";
                    newConfObj.MapoMes.IpAddr = MpIp.Replace($"{newConfObj.MapoMes.Transport}://", ""); // tolgo http/https...
                    newConfObj.MapoMes.ClientInstall = "SteamWare";

                }
                string MpUrl = fIni.ReadString("SERVER", "MPURL", "");
                if (!string.IsNullOrEmpty(MpUrl))
                {
                    newConfObj.MapoMes.BaseAppUrl = $"{MpUrl}/".Replace("//", "/");
                }
                string api = fIni.ReadString("SERVER", "APICLASS", "IOB");
                newConfObj.MapoMes.BaseApiClass = api;

                // copio conf ping come x server...
                newConfObj.MapoMes.DisabPing = newConfObj.Device.DisabPing;
                // verifico opzione per disabilitare FeepAlive (es non voglio keepalive e info IOB da acquisitori potenza che sovrascrivono dati IOB macchina)
                newConfObj.MapoMes.DisabKeepAlive = bool.Parse(fIni.ReadString("OPTPAR", "DISAB_KEEPALIVE", "false"));
                exclOptPar.Add("DISAB_KEEPALIVE");

                // Gestione contapezzi...
                newConfObj.Counters.EnabSendPzcBlock = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_SEND_PZC_BLOCK", "false"));
                newConfObj.Counters.EnableSetPzCount = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_PZ_CNT", "false"));
                newConfObj.Counters.EnableSetPzReq = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_PZ_REQ", "false"));
                newConfObj.Counters.MaxIncrPzCountPerc = fIni.ReadInteger("OPTPAR", "MAX_PZ_INCR_PERC", 300);
                newConfObj.Counters.ResetOnProdEnd = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_PZ_RESET_ENDPROD", "false"));
                newConfObj.Counters.ResetOnSetupStart = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_PZ_RESET", "false"));
                newConfObj.Counters.ResetOnSetupStop = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_PZ_RESET_stopSetup", "false"));
                newConfObj.Counters.SendPzcBlockMin = fIni.ReadInteger("OPTPAR", "MIN_SEND_PZC_BLOCK", 0);
                newConfObj.Counters.SendPzcBlockMax = fIni.ReadInteger("OPTPAR", "MAX_SEND_PZC_BLOCK", 100);
                newConfObj.Counters.SendPzCountMode = fIni.ReadString("OPTPAR", "SEND_PZCNT_MODE", "DIRECT");

                exclOptPar.Add("ENABLE_SEND_PZC_BLOCK");
                exclOptPar.Add("ENABLE_PZ_CNT");
                exclOptPar.Add("ENABLE_PZ_REQ");
                exclOptPar.Add("ENABLE_PZ_RESET_ENDPROD");
                exclOptPar.Add("ENABLE_PZ_RESET");
                exclOptPar.Add("ENABLE_PZ_RESET_stopSetup");
                exclOptPar.Add("MAX_PZ_INCR_PERC");
                exclOptPar.Add("MAX_SEND_PZC_BLOCK");
                exclOptPar.Add("MIN_SEND_PZC_BLOCK");
                exclOptPar.Add("SEND_PZCNT_MODE");

                // init special a prescindere...
                newConfObj.Special = new SpecializedDto();

                // FTP
                string ftpServer = fIni.ReadString("OPTPAR", "FTP_SERVER", "");
                if (!string.IsNullOrEmpty(ftpServer))
                {
                    if (newConfObj.Special.FtpConf == null)
                    {
                        newConfObj.Special.FtpConf = new FtpDto()
                        {
                            Server = ftpServer,
                            User = fIni.ReadString("OPTPAR", "FTP_USER", ""),
                            Passwd = fIni.ReadString("OPTPAR", "FTP_PWD", ""),
                            CertValue = fIni.ReadString("OPTPAR", "FTP_CERT", ""),
                            CsvAddHeader = bool.Parse(fIni.ReadString("OPTPAR", "CSV_ADD_HEADER", "false")),
                            SkipCert = bool.Parse(fIni.ReadString("OPTPAR", "FTP_SKIP", "true")),
                            DirLocal = fIni.ReadString("OPTPAR", "FTP_LOC_DIR", ""),
                            DirRemote = fIni.ReadString("OPTPAR", "FTP_REM_DIR", ""),
                        };
                        // continuo col resto
                    }
                }
                exclOptPar.Add("FTP_SERVER");
                exclOptPar.Add("FTP_USER");
                exclOptPar.Add("FTP_PWD");
                exclOptPar.Add("FTP_CERT");
                exclOptPar.Add("CSV_ADD_HEADER");
                exclOptPar.Add("FTP_SKIP");
                exclOptPar.Add("FTP_LOC_DIR");
                exclOptPar.Add("FTP_REM_DIR");


                // allarmi...
                string jsonAlarms = fIni.ReadString("OPTPAR", "ALARM_CONF", "");
                if (!string.IsNullOrEmpty(jsonAlarms))
                {
                    string alarmsFileName = Path.Combine(dirPath, jsonAlarms);
                    if (File.Exists(alarmsFileName))
                    {
                        newConfObj.lgInfo($"Apertura file {alarmsFileName}");
                        StreamReader reader = new StreamReader(alarmsFileName);
                        string jsonData = reader.ReadToEnd();
                        reader.Dispose();
                        if (!string.IsNullOrEmpty(jsonData))
                        {
                            newConfObj.lgInfo($"File json ALLARMI composto da {jsonData.Length} caratteri");
                            try
                            {
                                newConfObj.lgInfo("loadMemConf | Inizio setup allarmi");
                                newConfObj.Alarms = new AlarmDto();
                                newConfObj.Alarms.AlarmMaps = JsonConvert.DeserializeObject<List<BaseAlarmConf>>(jsonData);
                                newConfObj.lgInfo("loadMemConf | Fine setup allarmi");
                            }
                            catch (Exception exc)
                            {
                                newConfObj.lgError($"Eccezione in decodifica conf json degli ALLARMI{Environment.NewLine}{exc}");
                            }
                        }
                        else
                        {
                            newConfObj.lgError("Errore in loadMemConf: file ALLARMI json vuoto!");
                        }
                    }
                    else
                    {
                        newConfObj.lgError($"File allarmi non trovato: {alarmsFileName}");
                    }
                }
                else
                {
                    newConfObj.lgInfo("loadMemConf: non trovata opzione ALARM_CONF in file INI");
                }
                exclOptPar.Add("ALARM_CONF");

                // file conf...
                if (!string.IsNullOrEmpty(fIni.ReadString("OPTPAR", "FILE_IMPORT_FOLDER", "")))
                {
                    if (newConfObj.Special.FileConf == null)
                    {
                        newConfObj.Special.FileConf = new FileDto()
                        {
                            ImportFolder = fIni.ReadString("OPTPAR", "FILE_IMPORT_FOLDER", ""),
                            ImportType = fIni.ReadString("OPTPAR", "FILE_IMPORT_TYPE", "*.xslx"),
                            ArchiveFolder = fIni.ReadString("OPTPAR", "FILE_ARCHIVE_FOLDER", ""),
                            ExcelToolDirPath = fIni.ReadString("OPTPAR", "EXCL_TOOL_PATH", "")
                        };
                    }
                }
                exclOptPar.Add("FILE_IMPORT_FOLDER");
                exclOptPar.Add("FILE_IMPORT_TYPE");
                exclOptPar.Add("FILE_ARCHIVE_FOLDER");
                exclOptPar.Add("EXCL_TOOL_PATH");

                // Altro
                newConfObj.IobMan.MinDeltaSec = fIni.ReadInteger("IOB", "MinDeltaSec", 6);


                // alla fine verifico eventuali eccezioni alla gestione pzCount con abilitazione esplicita
                if (!string.IsNullOrEmpty(fIni.ReadString("OPTPAR", "ENABLE_PZCOUNT", "")))
                {
                    newConfObj.Device.EnabPzCount = bool.Parse(fIni.ReadString("OPTPAR", "ENABLE_PZCOUNT", "false"));
                }
                // o al contrario con disabilitazione esplicita
                if (!string.IsNullOrEmpty(fIni.ReadString("OPTPAR", "DISABLE_PZCOUNT", "")))
                {
                    newConfObj.Device.EnabPzCount = !bool.Parse(fIni.ReadString("OPTPAR", "DISABLE_PZCOUNT", "false"));
                }
                // gestione override idx articoli...
                if (!string.IsNullOrEmpty(fIni.ReadString("OPTPAR", "NUM_ART_CHR_TRIM", "")))
                {
                    newConfObj.Device.NumArtCharTrim = bool.Parse(fIni.ReadString("OPTPAR", "NUM_ART_CHR_TRIM", "false"));
                }
                exclOptPar.Add("ENABLE_PZCOUNT");
                exclOptPar.Add("DISABLE_PZCOUNT");
                exclOptPar.Add("NUM_ART_CHR_TRIM");



                bool doExclOptPar = bool.Parse(fIni.ReadString("OPTPAR", "DO_EXCLUDE_OPT_PAR", "true"));
                exclOptPar.Add("DO_EXCLUDE_OPT_PAR");


                // se è tipo ping 
                if (newConfObj.General.IobType == tipoAdapter.PingWatchdog)
                {
                    // verifico SE ci siano in optPar indicazioni x valori da inviare al poweron/poweroff
                    newConfObj.Special.PingConf = new PingDto();
                    newConfObj.Special.PingConf.B_PowerOff = fIni.ReadInteger("OPTPAR", "B_IN_OFF", 0);
                    newConfObj.Special.PingConf.B_PowerOn = fIni.ReadInteger("OPTPAR", "B_IN_ON", 1);
                    newConfObj.Special.PingConf.VetoCheckSec = fIni.ReadInteger("OPTPAR", "VETO_PING_SEC", 5);
                }
                exclOptPar.Add("B_IN_OFF");
                exclOptPar.Add("B_IN_ON");
                exclOptPar.Add("VETO_PING_SEC");


                // caricamento finale OptPar
                Dictionary<string, string> optParRead = new Dictionary<string, string>();
                string[] optParRows = fIni.ReadSection("OPTPAR");
                if (optParRows.Length > 0)
                {
                    string[] kvp;
                    if (doExclOptPar)
                    {
                        try
                        {
                            foreach (var item in optParRows)
                            {
                                kvp = item.Split('=');
                                // escludo valori già acquisiti...
                                if (!exclOptPar.Contains(kvp[0]))
                                {
                                    optParRead.Add(kvp[0], kvp[1]);
                                }
                            }
                            newConfObj.lgDebug($"Caricati {optParRead.Count} parametri opzionali da OPTPAR");
                        }
                        catch (Exception exc)
                        {
                            newConfObj.lgError($"EXCEPTION in fase di lettura OPTPAR: {Environment.NewLine}{exc}");
                        }
                    }
                    else
                    {
                        foreach (var item in optParRows)
                        {
                            kvp = item.Split('=');
                            optParRead.Add(kvp[0], kvp[1]);
                        }
                    }
                }
                // riordino alfabeticamente
                optParRead = optParRead.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value);
                newConfObj.OptPar = optParRead;


                // verifico se ho conf json speciali: LUT decodifica PARAMETRI (uso una delle chaivi ammesse...)
                List<string> memKeyNames = new List<string>()
                {
                    "PARAM_CONF",
                    "OPC_PARAM_CONF",
                    "MTC_PARAM_CONF"
                };
                string memFileName = "";
                foreach (var memKey in memKeyNames)
                {
                    if (optParRead.ContainsKey(memKey))
                    {
                        memFileName = optParRead[memKey];
                        newConfObj.lgDebug($"loadMemConf: trovata opzione per recupero IobConfTree.Memory in file INI | key: {memKey} | file:{memFileName} | valori testati: {String.Join(" | ", memKeyNames.ToArray())}");
                        break;
                    }
                }

                if (string.IsNullOrEmpty(memFileName))
                {
                    newConfObj.lgInfo($"loadMemConf: non trovata opzione per recupero IobConfTree.Memory in file INI, valori testati: {String.Join(" | ", memKeyNames.ToArray())}");
                }

                string jsonFileName = Path.Combine(dirPath, memFileName);
                if (File.Exists(jsonFileName))
                {
                    newConfObj.lgInfo($"Apertura file {jsonFileName}");
                    using (StreamReader reader = new StreamReader(jsonFileName))
                    {
                        string jsonData = reader.ReadToEnd();
                        if (!string.IsNullOrEmpty(jsonData))
                        {
                            if (newConfObj.Memory == null)
                            {
                                newConfObj.Memory = new plcMemMapExt();
                            }
                            try
                            {
                                newConfObj.Memory = JsonConvert.DeserializeObject<plcMemMapExt>(jsonData);
                            }
                            catch (Exception exc)
                            {
                                newConfObj.lgError($"Eccezione in decodifica conf json{Environment.NewLine}{exc}");
                            }
                        }
                        else
                        {
                            newConfObj.lgError("Errore in loadMemConf: file json vuoto!");
                        }
                    }
                }

                // parametri opzionali Siemens
                if (!string.IsNullOrEmpty(fIni.ReadString("CNC", "CPUTYPE", "")))
                {
                    newConfObj.Special.SiemensConf = new SiemensDto();
                    // CPU Siemens
                    newConfObj.Special.SiemensConf.CpuType = fIni.ReadString("CNC", "CPUTYPE", "");
                    newConfObj.Special.SiemensConf.Rack = (short)fIni.ReadInteger("CNC", "RACK", 0);
                    newConfObj.Special.SiemensConf.Slot = (short)fIni.ReadInteger("CNC", "SLOT", 0);
                    // memoria Siemens!
                    newConfObj.Special.SiemensConf.MemAddrRead = fIni.ReadString("MEMORY", "ADDR_READ", "");
                    newConfObj.Special.SiemensConf.MemAddrWrite = fIni.ReadString("MEMORY", "ADDR_WRITE", "");
                    newConfObj.Special.SiemensConf.MemSizeRead = fIni.ReadInteger("MEMORY", "SIZE_READ", 0);
                    newConfObj.Special.SiemensConf.MemSizeWrite = fIni.ReadInteger("MEMORY", "SIZE_WRITE", 0);
                }


                // parametri opzionali OpcUA
                if (optParRead.ContainsKey("OPC_PARAM_CONF"))
                {
                    string opcParamConf = optParRead["OPC_PARAM_CONF"];
                    if (!string.IsNullOrEmpty(opcParamConf))
                    {
                        string jsonOpcFileName = Path.Combine(dirPath, opcParamConf);
                        if (File.Exists(jsonOpcFileName))
                        {
                            newConfObj.lgInfo($"Apertura file {jsonOpcFileName}");
                            using (StreamReader reader = new StreamReader(jsonOpcFileName))
                            {
                                string jsonData = reader.ReadToEnd().Replace("\n", "").Replace("\r", "");
                                if (!string.IsNullOrEmpty(jsonData))
                                {
                                    newConfObj.lgDebug($"File json composto da {jsonData.Length} caratteri");
                                    if (newConfObj.Special.OpcUaConf == null)
                                    {
                                        newConfObj.Special.OpcUaConf = new OpcUaParamConf();
                                    }
                                    try
                                    {
                                        newConfObj.Special.OpcUaConf = JsonConvert.DeserializeObject<OpcUaParamConf>(jsonData);
                                        newConfObj.lgDebug($"Decodifica aree OpcUaParamConf: trovati {newConfObj.Special.OpcUaConf.paramsEndThresh.Count} valori paramsEndThresh");
#if false
                                        // sistemo se ci sono dati memMap...
                                        if (newConfObj.Memory == null)
                                        {
                                            newConfObj.Memory = new plcMemMapExt();
                                        }
                                        try
                                        {
                                            newConfObj.Memory = JsonConvert.DeserializeObject<plcMemMapExt>(jsonData);
                                        }
                                        catch (Exception exc)
                                        {
                                            newConfObj.lgError($"Eccezione in decodifica conf json{Environment.NewLine}{exc}");
                                        } 
#endif

                                        // elimino conf memoria da area OPC che tanto è già OK...
                                        newConfObj.Special.OpcUaConf.mMapRead = new Dictionary<string, MapoSDK.dataConfTSVC>();
                                        newConfObj.Special.OpcUaConf.mMapWrite = new Dictionary<string, MapoSDK.dataConf>();
                                    }
                                    catch (Exception exc)
                                    {
                                        newConfObj.lgError($"Eccezione in decodifica conf json OPC-UA{Environment.NewLine}{exc}");
                                    }
                                }
                                else
                                {
                                    newConfObj.lgError("Errore in loadOpcUaConf: file json vuoto!");
                                }
                            }
                        }
                    }
                }

                // parametri opzionali BankConf
                string[] memSection = fIni.ReadSection("MEMORY");
                // in primis SE ho qualcosa...
                if (memSection != null && memSection.Count() > 0)
                {
                    // trasformo in array...
                    Dictionary<string, string> memDict = new Dictionary<string, string>();
                    foreach (var item in memSection)
                    {
                        // verifica preliminare NON sia commento (inizia per ";")
                        if (!item.StartsWith(";") && item.Contains("="))
                        {
                            var KVP = item.Split('=');
                            memDict.Add(KVP[0], KVP[1]);
                        }
                    }
                    // ora se ho qualcosa proseguo...
                    if (memDict.Count() > 0)
                    {
                        // cerco dati x popolare SigLUT
                        foreach (var item in memDict.Where(x => x.Key.StartsWith("BIT")))
                        {
                            if (newConfObj.Device.SigLUT.ContainsKey(item.Key))
                            {
                                newConfObj.Device.SigLUT[item.Key] = item.Value;
                            }
                            else
                            {
                                newConfObj.Device.SigLUT.Add(item.Key, item.Value);
                            }
                        }
                        // cerco dati specifici x popolare l'area Memoria (Fanuc/Mitsubischi/...)
                        if (memDict.Where(x => x.Key.StartsWith("AREA") || x.Key.StartsWith("PAR") || x.Key.StartsWith("SIGN")).Count() > 0)
                        {
                            // init info bank memoria...
                            if (newConfObj.Special.BankConf == null)
                            {
                                newConfObj.Special.BankConf = new MemBankDto();
                            }
                            // inizio setup prendendo quelli con valori addrSize
                            foreach (var item in memDict.Where(x => x.Key.EndsWith("SIZE")))
                            {
                                int addrSize = 0;
                                int.TryParse(item.Value, out addrSize);
                                // salvo solo quelli con valori addrSize > 0
                                if (addrSize > 0)
                                {
                                    string mId = item.Key.Replace("_SIZE", "");
                                    // cerco record inizio
                                    var valStart = memDict.Where(x => x.Key == item.Key.Replace("_SIZE", "_START")).Select(x => x.Value).FirstOrDefault();
                                    if (!string.IsNullOrEmpty(valStart))
                                    {
                                        int addrStart = 0;
                                        int.TryParse(valStart, out addrStart);
                                        var memArea = new Mem.MemAreaDto()
                                        {
                                            Start = addrStart,
                                            Size = addrSize
                                        };
                                        if (newConfObj.Special.BankConf.AreaConf.ContainsKey(mId))
                                        {
                                            newConfObj.Special.BankConf.AreaConf[mId] = memArea;
                                        }
                                        else
                                        {
                                            newConfObj.Special.BankConf.AreaConf.Add(mId, memArea);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                // cerco in OPTPAR la conf json del dizionario StepActionList
                if (optParRead.ContainsKey("STEP_ACTION_CONF"))
                {
                    string opcParamConf = optParRead["STEP_ACTION_CONF"];
                    if (!string.IsNullOrEmpty(opcParamConf))
                    {
                        string jsonStepActFileName = Path.Combine(dirPath, opcParamConf);
                        if (File.Exists(jsonStepActFileName))
                        {
                            newConfObj.lgInfo($"Apertura file {jsonStepActFileName}");
                            using (StreamReader reader = new StreamReader(jsonStepActFileName))
                            {
                                string jsonData = reader.ReadToEnd().Replace("\n", "").Replace("\r", "");
                                if (!string.IsNullOrEmpty(jsonData))
                                {
                                    newConfObj.lgDebug($"File json composto da {jsonData.Length} caratteri");
                                    if (newConfObj.Actions == null)
                                    {
                                        newConfObj.Actions = new ActionDto();
                                        //newConfObj.Actions.StepActionList = new Dictionary<string, StepActionDto>();
                                        try
                                        {
                                            newConfObj.Actions = JsonConvert.DeserializeObject<ActionDto>(jsonData);
                                        }
                                        catch (Exception exc)
                                        {
                                            newConfObj.lgError($"Eccezione in decodifica conf json STEP_ACTION_CONF{Environment.NewLine}{exc}");
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                // salvo eventuale memTrace...
                if (!string.IsNullOrEmpty(fIni.ReadString("OPTPAR", "MEM_2_TRACE", "")))
                {
                    if (newConfObj.Special.BankConf == null)
                    {
                        newConfObj.Special.BankConf = new MemBankDto();
                    }
                    newConfObj.Special.BankConf.Mem2Trace = fIni.ReadString("OPTPAR", "MEM_2_TRACE", "");
                    exclOptPar.Add("MEM_2_TRACE");
                }

                // gestione speciale x type MBus
                if ($"{newConfObj.General.IobType}".StartsWith("MODBUS_TCP"))
                {
                    ModBusTcpParamConf newMBusPar = new ModBusTcpParamConf()
                    {
                        memAddrRead = fIni.ReadString("MEMORY", "ADDR_READ", ""),
                        memAddrWrite = fIni.ReadString("MEMORY", "ADDR_WRITE", ""),
                        memSizeRead = fIni.ReadInteger("MEMORY", "SIZE_READ", 0),
                        memSizeWrite = fIni.ReadInteger("MEMORY", "SIZE_WRITE", 0),
                        holdRegBaseAddr = fIni.ReadInteger("MEMORY", "HR_BASE_ADDR", 40001),
                        useCalcBaseAddr = fIni.ReadBoolean("MEMORY", "CALC_BASE_ADDR", true),
                        // valore delta base e shift
                        deltaBase = fIni.ReadInteger("MEMORY", "DELTA_BASE", 0),
                        indexLutCorr = fIni.ReadInteger("MEMORY", "INDEX_LUT_CORR", 0),
                        // verifico eventuale parametro memoria estesa (non 0...10'000 ma 0...xFFFF=65536)
                        modbusExtReg = fIni.ReadBoolean("MEMORY", "MODBUS_EXT_REG")
                    };
                    newConfObj.Special.ModbusConf = newMBusPar;
                }

            }
            catch (Exception exc)
            {
                newConfObj.lgError($"EXCEPTION in decodifica IobConfTree: {Environment.NewLine}{exc}");
            }
            return newConfObj;
        }

        /// <summary>
        /// Calcola la scadenza in ms del timer richiesto
        /// </summary>
        /// <param name="ciclo">Tipo di ciclo richiesto x estrarre da obj conf</param>
        /// <param name="percFact">Fattore di perturbazione random come valore +/- da aggiungere in %
        /// default 0 = deterministico
        /// Range ammesso: 0..0.5</param>
        /// <returns></returns>
        public int TimerMs(gatherCycle ciclo, double percFact = 0)
        {
            // default a 1sec...
            int answ = 1000;
            // verifica quale valore recuperare
            switch (ciclo)
            {
                case gatherCycle.VHF:
                    answ = General.Timers.MsVHF;
                    break;
                case gatherCycle.HF:
                    answ = General.Timers.MsHF;
                    break;
                case gatherCycle.MF:
                    answ = General.Timers.MsMF;
                    break;
                case gatherCycle.LF:
                    answ = General.Timers.MsLF;
                    break;
                case gatherCycle.VLF:
                    answ = General.Timers.MsVLF;
                    break;
                default:
                    break;
            }
            // se richeisto perturbo...
            if (percFact != 0)
            {
                // prendo valore in modulo
                percFact = Math.Abs(percFact);
                // verifico sia < 50%...
                percFact = percFact > 0.5 ? 0.5 : percFact;
                // mi baso su un fattore 1'000 da perturbare
                int mult = 1000;
                answ = (answ * rand.Next((int)(mult * (1 - percFact)), (int)(mult * (1 + percFact))) / mult);
            }
            return answ;
        }

        /// <summary>
        /// Info generali delative all'IOB impiegato
        /// </summary>
        public IobDto General { get; set; } = new IobDto();

        /// <summary>
        /// Parametri server Mapo MES
        /// </summary>
        public ServerMapoDto MapoMes { get; set; } = new ServerMapoDto();

        /// <summary>
        /// Setup info verso IOB-MAN
        /// </summary>
        public IobManDto IobMan { get; set; } = new IobManDto();

        /// <summary>
        /// Gestione specifica azioni sul contapezzi
        /// </summary>
        public CounterDto Counters { get; set; } = new CounterDto();

        /// <summary>
        /// Gestione configurazione parametri relativi ai FluxLog
        /// </summary>
        public FluxLogDto FluxLog { get; set; } = new FluxLogDto();

        /// <summary>
        /// Configurazione specifica gestione ODL
        /// </summary>
        public OdlDto Odl { get; set; } = new OdlDto();

        /// <summary>
        /// Info relative al device interconnesso
        /// </summary>
        public DeviceDto Device { get; set; } = new DeviceDto();

        /// <summary>
        /// Struttura memoria PLC x lettura/scrittura
        /// </summary>
        public plcMemMapExt Memory { get; set; }

        /// <summary>
        /// Setup processing dati in ingresso (es: blink segnali)
        /// </summary>
        public InputSignalDto SignalProc { get; set; } = new InputSignalDto();

        /// <summary>
        /// Dati relativi ai parametri gestione tempo ciclo
        /// </summary>
        public TCDataDto TCDataConf { get; set; } = new TCDataDto();

        /// <summary>
        /// Configurazione Allarmi
        /// </summary>
        public AlarmDto Alarms { get; set; }

        /// <summary>
        /// Configurazione speciale comportamenti IOB (es setup)
        /// </summary>
        public ActionDto Actions { get; set; }

        /// <summary>
        /// Dizionario delle traduzioni termini chiave/valore
        /// </summary>
        public Dictionary<string, string> ItemTranslation { get; set; } = new Dictionary<string, string>();

        /// <summary>
        /// Recupera traduzoine item richiesta
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string ItemTranslationGet(string key)
        {
            string answ = "";
            if (ItemTranslation != null && ItemTranslation.ContainsKey(key))
            {
                answ = ItemTranslation[key];
            }
            return answ;
        }

        /// <summary>
        /// Dizionario dei parametri opzionali
        /// </summary>
        public Dictionary<string, string> OptPar { get; set; } = new Dictionary<string, string>();

        /// <summary>
        /// Recupera valore della chiave specifica richiesta
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string OptParGet(string key)
        {
            string answ = "";
            if (OptPar != null && OptPar.Count > 0 && OptPar.ContainsKey(key))
            {
                answ = OptPar[key];
            }
            else
            {
#if DEBUG
                lgError($"OptParGet | key not found: {key}");
#else
                lgDebug($"OptParGet | key not found: {key}");
#endif
            }
            return answ;
        }

        /// <summary>
        /// Configurazione speciale/opzionale per tipo IOB
        /// </summary>
        public SpecializedDto Special { get; set; }

        #region Metodi Serializzazione

        /// <summary>
        /// Restituisce conf serializzata in formato JSON
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public string GetJson()
        {
            string rawdata = JsonConvert.SerializeObject(this, Formatting.Indented);
            return rawdata;
        }
        /// <summary>
        /// Restituisce conf serializzata in formato YAML
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public string GetYaml()
        {
            // opzioni alternative: PascalCaseNamingConvention (iniziale masiucola) o lowerCaseNamingConvention
            var serializer = new SerializerBuilder()
                                .WithNamingConvention(CamelCaseNamingConvention.Instance)
                                .Build();
            var rawdata = serializer.Serialize(this);
            return rawdata;
        }

        #endregion

        #region Metodi Load/Save

        /// <summary>
        /// Scrive conf serializzata in formato JSON
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public bool SaveJson(string filePath)
        {
            bool answ = false;
            try
            {
                string rawdata = GetJson();
                File.WriteAllText(filePath, rawdata);
                answ = true;
            }
            catch
            { }
            return answ;
        }
        /// <summary>
        /// Scrive conf serializzata in formato YAML
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public bool SaveYaml(string filePath)
        {
            bool answ = false;
            try
            {
                var rawdata = GetYaml();
                File.WriteAllText(filePath, rawdata);

                answ = true;
            }
            catch
            { }
            return answ;
        }

        /// <summary>
        /// Invia a server conf serializzata in formato YAML
        /// </summary>
        /// <param name="urlSaveYaml">URL x chiamata salvtaggio yaml</param>
        /// <returns></returns>
        public bool SendConfYaml(string urlSaveYaml)
        {
            bool answ = false;
            try
            {
                string rawData = GetYaml();
                // prova anche ad inviare il contenuto al server IO...
                string resp = utils.ExecCallPostPlain($"{urlSaveYaml}", rawData);
                answ = resp == "OK";
            }
            catch
            { }
            return answ;
        }

        #endregion

        #region Logging

        /// <summary>
        /// oggetto logging
        /// </summary>
        protected Logger Log;// = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// Effettua logging DEBUG corretto impostanto anche la variabile IOB prima di scrivere...
        /// </summary>
        /// <param name="txt2log"></param>
        protected void lgDebug(string txt2log)
        {
            Log.Factory.Configuration.Variables["codIOB"] = General.FilenameIOB;
            Log.Debug(txt2log);
        }

        /// <summary>
        /// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere...
        /// </summary>
        /// <param name="txt2log"></param>
        protected void lgError(string txt2log)
        {
            if (!string.IsNullOrEmpty(txt2log))
            {
                Log.Factory.Configuration.Variables["codIOB"] = General.FilenameIOB;
                Log.Error(txt2log);
            }
        }

        /// <summary>
        /// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
        /// </summary>
        /// <param name="txt2log"></param>
        protected void lgInfo(string txt2log)
        {
            Log.Factory.Configuration.Variables["codIOB"] = General.FilenameIOB;
            Log.Info(txt2log);
        }

        /// <summary>
        /// Effettua logging TRACE corretto impostanto anche la variabile IOB prima di scrivere...
        /// </summary>
        /// <param name="txt2log"></param>
        protected void lgTrace(string txt2log)
        {
            Log.Factory.Configuration.Variables["codIOB"] = General.FilenameIOB;
            Log.Trace(txt2log);
        }

        #endregion

        #region Area Private

        /// <summary>
        /// Generatore random x gestione non deterministica
        /// </summary>
        private Random rand = new Random();

        #endregion Area Private
    }
}