namespace MapoDataFiller.Filler { public class InterClays { #region Public Properties public SimSetup currSetup { get; set; } = new SimSetup(); #endregion Public Properties #region Public Methods /// /// Restituisce elenco righe da caricare sul DB dato periodo indicato /// /// /// /// public SimBlock GetDataRows(string CodIOB, DayConf currDay) { string fileConf = Path.Combine("Conf", $"{CodIOB}.json"); if (File.Exists(fileConf)) { currSetup = SimSetup.readConf(fileConf); } SimBlock answ = new SimBlock(); int idxEv = 0; int idxFl = 0; bool doProd = false; List nextRowsFL = new List(); List nextRowsEV = new List(); int valReq = 0; int valCurr = 0; // simulo periodi int numPer = rnd.Next(10, numPerMax); // calcolo durata media periodi in minuti double avgDurPer = (currDay.dtEnd.Subtract(currDay.dtStart).TotalMinutes) / numPer; double preDelay = avgDurPer / 2; // imposto cursore DateTime dtStart = currDay.dtStart.AddMinutes(preDelay); DateTime dtEnd = dtStart; // per prima cosa aggiungo start dopo un delay di max 1/2 periodo... answ.FlList.Add($"{CodIOB};{dtStart:yyyy-MM-dd HH:mm:ss.fff};IOB-STATUS;IOB Started;{idxFl++}"); answ.EvList.Add($"{CodIOB};{dtStart:yyyy-MM-dd HH:mm:ss.fff};14;ND;[{idxEv++}] 00;0;-"); // calcolo durata successivi while (dtEnd < currDay.dtEnd) { dtStart = dtEnd.AddSeconds(rnd.Next(25, 35)); dtEnd = dtStart.AddMinutes(avgDurPer * rnd.Next(600, 1400) / 1000); // verifico il tipo di CodIOB if (CodIOB == "INTERCL_01") { // tiro a dadi x decidere SE LAVORA periodo... 50% dei casi nel periodo TRACCIATO doProd = (rnd.Next(0, 100) <= 40); // FluxLOG: genero righe FL x periodo e sommo nextRowsFL = IC_ESS_getFlRows(CodIOB, dtStart, dtEnd, doProd, ref idxFl); // EvList: genero righe EV x periodo e sommo nextRowsEV = IC_ESS_getEvRows(CodIOB, dtStart, dtEnd, doProd, ref idxEv); } else if (CodIOB == "INTERCL_02") { // verifico se DEVO finire caricamento... if (valCurr < valReq) { doProd = true; } else { // tiro a dadi x decidere SE ho pesate nel periodo... 30% dei casi doProd = (rnd.Next(0, 100) <= 30); // FluxLOG: SE ho pesate --> genero target valReq = doProd ? rnd.Next(valMin / valStep, valMax / valStep) * valStep : 0; } // FluxLOG: genero righe FL x periodo e sommo nextRowsFL = IC_OX_getFlRows(CodIOB, dtStart, dtEnd, doProd, valReq, ref idxFl, ref valCurr); // EvList: genero righe EV x periodo e sommo nextRowsEV = IC_OX_getEvRows(CodIOB, dtStart, dtEnd, doProd, ref idxEv); } // accodo FL answ.FlList.AddRange(nextRowsFL); // accodo EV answ.EvList.AddRange(nextRowsEV); } // aggiungo chiusura eventi... dtStart = dtEnd.AddSeconds(rnd.Next(25, 35)); answ.EvList.Add($"{CodIOB};{dtStart:yyyy-MM-dd HH:mm:ss.fff};15;ND;[{idxEv++}] 01;0;-"); dtStart = dtEnd.AddSeconds(rnd.Next(80, 180)); answ.EvList.Add($"{CodIOB};{dtStart:yyyy-MM-dd HH:mm:ss.fff};14;ND;[{idxEv++}] 00;0;-"); // ritorno return answ; } #endregion Public Methods #region Protected Fields protected int numPerMax = 50; protected int numSec = 30; protected Random rnd = new Random(); protected int stepMax = 150; protected int stepMin = 50; protected int valMax = 15000; protected int valMin = 500; protected int valStep = 10; protected int waitMax = 240; protected int waitMin = 10; #endregion Protected Fields #region Protected Methods /// /// Simulo un blocco dati FL /// /// /// /// /// /// /// protected List IC_OX_getEvRows(string CodIOB, DateTime dtStart, DateTime dtEnd, bool doProd, ref int idxCount) { List rows = new List(); DateTime dtCurs = dtStart.AddMilliseconds(rnd.Next(1000, 60000)); string currRow = ""; // reset counter idxCount = idxCount <= 9999 ? idxCount : 0; // se produce registro poweron... if (doProd) { currRow = $"{CodIOB};{dtStart.AddSeconds(5):yyyy-MM-dd HH:mm:ss.fff};16;ND;[{idxCount++}] 23;0;-"; rows.Add(currRow); currRow = $"{CodIOB};{dtEnd.AddSeconds(-5):yyyy-MM-dd HH:mm:ss.fff};15;ND;[{idxCount++}] 01;0;-"; rows.Add(currRow); } // se NON produce alterno valori spenta/accesa else { // genera i dati secondo lo schema configurato... con periodo da 15" a 15 min di pausa while (dtCurs < dtEnd) { currRow = $"{CodIOB};{dtCurs:yyyy-MM-dd HH:mm:ss.fff};15;ND;[{idxCount++}] 01;0;-"; rows.Add(currRow); dtCurs = dtCurs.AddSeconds(rnd.Next(60 * 5, 60 * 15)); currRow = $"{CodIOB};{dtCurs:yyyy-MM-dd HH:mm:ss.fff};14;ND;[{idxCount++}] 00;0;-"; rows.Add(currRow); dtCurs = dtCurs.AddMilliseconds(rnd.Next(500, 5000)); // reset counter idxCount = idxCount <= 9999 ? idxCount : 0; } } return rows; } /// /// Simulo un blocco dati FL /// /// /// /// /// /// /// /// /// protected List IC_OX_getFlRows(string CodIOB, DateTime dtStart, DateTime dtEnd, bool doProd, int valReq, ref int idxCount, ref int valCurr) { List rows = new List(); DateTime dtCurs = dtStart; int valTo = doProd ? valReq + rnd.Next(0, valStep) * valStep : 0; valCurr = valReq > 0 ? valCurr : 0; string currRow = ""; // reset counter idxCount = idxCount <= 9999 ? idxCount : 0; // genera i dati secondo lo schema configurato... while (dtCurs < dtEnd) { currRow = $"{CodIOB};{dtCurs:yyyy-MM-dd HH:mm:ss.fff};kgImp;{valReq};{idxCount++}"; rows.Add(currRow); dtCurs = dtCurs.AddMilliseconds(rnd.Next(50, 300)); currRow = $"{CodIOB};{dtCurs:yyyy-MM-dd HH:mm:ss.fff};kgAct;{valCurr};{idxCount++}"; rows.Add(currRow); dtCurs = dtCurs.AddMilliseconds(rnd.Next(27000, 33000)); // incremento peso... SE <= max... if (valCurr < valTo) { valCurr += rnd.Next(stepMin / valStep, stepMax / valStep) * valStep; } // reset counter idxCount = idxCount <= 9999 ? idxCount : 0; } return rows; } /// /// Simulo un blocco dati FL /// /// /// /// /// /// /// protected List IC_ESS_getEvRows(string CodIOB, DateTime dtStart, DateTime dtEnd, bool doProd, ref int idxCount) { List rows = new List(); DateTime dtCurs = dtStart.AddMilliseconds(rnd.Next(1000, 60000)); string currRow = ""; // reset counter idxCount = idxCount <= 9999 ? idxCount : 0; var fullDuration = dtEnd.Subtract(dtStart).TotalMinutes; // se produce registro riscaldamento + poweron... if (doProd) { // calcolo 1/8 = 12.5% riscaldamento... currRow = $"{CodIOB};{dtStart.AddSeconds(1):yyyy-MM-dd HH:mm:ss.fff};40;ND;[{idxCount++}] 34;0;-"; rows.Add(currRow); // ...il resto lavoro... partendo a 1/8 currRow = $"{CodIOB};{dtStart.AddMinutes(fullDuration / 8):yyyy-MM-dd HH:mm:ss.fff};16;ND;[{idxCount++}] 13;0;-"; rows.Add(currRow); currRow = $"{CodIOB};{dtEnd.AddSeconds(-1):yyyy-MM-dd HH:mm:ss.fff};15;ND;[{idxCount++}] 12;0;-"; rows.Add(currRow); } // se NON produce alterno valori spenta/accesa else { /* * genera i dati con durata secondo pareto standard... * - 10% --> 24: manuale * - 30% --> 15: fermo generico * - 60% --> 14: spenta */ // manuale currRow = $"{CodIOB};{dtStart.AddSeconds(1):yyyy-MM-dd HH:mm:ss.fff};24;ND;[{idxCount++}] 24;0;-"; rows.Add(currRow); // fermo generico dopo 10% currRow = $"{CodIOB};{dtStart.AddMinutes(fullDuration / 10):yyyy-MM-dd HH:mm:ss.fff};15;ND;[{idxCount++}] 12;0;-"; rows.Add(currRow); // spenta dopo 40% currRow = $"{CodIOB};{dtStart.AddMinutes(fullDuration * 4 / 10):yyyy-MM-dd HH:mm:ss.fff};15;ND;[{idxCount++}] 12;0;-"; rows.Add(currRow); // reset counter idxCount = idxCount <= 9999 ? idxCount : 0; } return rows; } /// /// Simulo un blocco dati FL /// /// /// /// /// /// /// /// /// protected List IC_ESS_getFlRows(string CodIOB, DateTime dtStart, DateTime dtEnd, bool doProd, ref int idxCount) { List rows = new List(); // inizio spostando di 5-10 sec avanti inizio DateTime dtCurs = dtStart.AddSeconds(rnd.Next(5, 10)); int simInt = 0; double simReal = 0; double simDTime = 0; // se è do prod --> simulo 50° percentile in su, altrimenti primi 50 percentili... string currRow = ""; // reset counter idxCount = idxCount <= 9999 ? idxCount : 0; // genera i dati secondo lo schema configurato... while (dtCurs < dtEnd) { // in primis genero gli items INT if (currSetup.SetupSimInt.Count > 0) { foreach (var item in currSetup.SetupSimInt) { simInt = simValInt(doProd, item.Key, item.Value); currRow = $"{CodIOB};{dtCurs:yyyy-MM-dd HH:mm:ss.fff};{item.Key};{simInt};{idxCount++}"; rows.Add(currRow); dtCurs = dtCurs.AddMilliseconds(rnd.Next(10, 100)); idxCount = idxCount <= 9999 ? idxCount : 0; } } // poi gli item REAL if (currSetup.SetupSimReal.Count > 0) { foreach (var item in currSetup.SetupSimReal) { simReal = simValReal(doProd, item.Key, item.Value); currRow = $"{CodIOB};{dtCurs:yyyy-MM-dd HH:mm:ss.fff};{item.Key};{simReal};{idxCount++}"; rows.Add(currRow); dtCurs = dtCurs.AddMilliseconds(rnd.Next(10, 100)); idxCount = idxCount <= 9999 ? idxCount : 0; } } // infine certo gli items basati su datetime if (currSetup.SetupDtData.Count > 0 && doProd) { foreach (var item in currSetup.SetupDtData) { simDTime = simValData(dtCurs, item.Key, item.Value); currRow = $"{CodIOB};{dtCurs:yyyy-MM-dd HH:mm:ss.fff};{item.Key};{simDTime};{idxCount++}"; rows.Add(currRow); dtCurs = dtCurs.AddMilliseconds(rnd.Next(10, 100)); idxCount = idxCount <= 9999 ? idxCount : 0; } } dtCurs = dtCurs.AddMilliseconds(rnd.Next(87000, 93000)); // reset counter idxCount = idxCount <= 9999 ? idxCount : 0; } return rows; } /// /// Simulazione valore int secondo tab transcodifica: /// doProd=true --> simulo 50° percentile in su, altrimenti primi 50 percentili... /// /// /// /// /// protected int simValInt(bool doProd, string codFlux, Dictionary transcMap) { int result = 0; // se è in prod --> 50° perc in su... int rawSim = rnd.Next(0, 50) + (doProd ? 50 : 0); // transcodifico var val0 = transcMap .Where(x => x.Key <= rawSim) .OrderByDescending(x => x.Value) .FirstOrDefault(); var val1 = transcMap .Where(x => x.Key >= rawSim) .OrderBy(x => x.Value) .FirstOrDefault(); result = val0.Value + (int)Math.Round((double)(val1.Value - val0.Value) * (rawSim - val0.Key) / (val1.Key - val0.Key), 0); return result; } /// /// Simulazione valore REAL secondo tab transcodifica: /// doProd=true --> simulo 50° percentile in su, altrimenti primi 50 percentili... /// /// /// /// /// protected double simValReal(bool doProd, string codFlux, Dictionary transcMap) { double result = 0; // se è in prod --> 50° perc in su... int rawSim = rnd.Next(0, 50) + (doProd ? 50 : 0); // transcodifico var val0 = transcMap .Where(x => x.Key <= rawSim) .OrderByDescending(x => x.Value) .FirstOrDefault(); var val1 = transcMap .Where(x => x.Key >= rawSim) .OrderBy(x => x.Value) .FirstOrDefault(); result = Math.Round(val0.Value + (val1.Value - val0.Value) * (rawSim - val0.Key) / (val1.Key - val0.Key), 2); return result; } /// /// Simulazione valore Real dt-based secondo tab transcodifica /// /// /// /// /// protected double simValData(DateTime dtCurs, string codFlux, Dictionary transcMap) { double result = 0; int rawSim = rnd.Next(-5000, 5000); // perturbo la data in ms per +/-5 sec dtCurs = dtCurs.AddMilliseconds(rawSim); // transcodifico var val0 = transcMap .Where(x => x.Key <= dtCurs) .OrderByDescending(x => x.Value) .FirstOrDefault(); var val1 = transcMap .Where(x => x.Key >= dtCurs) .OrderBy(x => x.Value) .FirstOrDefault(); // prendo valore intervallo calcolato... result = Math.Round(val0.Value + (val1.Value - val0.Value) * (dtCurs - val0.Key) / (val1.Key - val0.Key), 2); return result; } #endregion Protected Methods } }