using IOB_UT; using MapoSDK; using System; using System.Collections.Generic; namespace IOB_WIN { /// /// Configuraizone eventi da simulare /// public class simPar { /// /// Attesa per evento /// public int wait = 10; /// /// Durata dell'evento /// public int duration = 1; /// /// DateTime ultimo evento /// public DateTime lastEv = DateTime.Now; } public class IobSimula : IobGeneric { /// /// pallet corrente /// protected int cP = 1; /// /// pallet successivo (next) /// protected int nP = 1; /// /// periodo base del simulatore (in secondi) /// protected int periodoMSec = 1000; /// /// BOOL: indica se simulare powerOn/Off (bit 0 e 1) compresi WarmUp e CoolDown /// protected bool simPowerOnOff; /// /// Ora dia ccensione (standard) /// public int tOn = 6; /// /// Ora spegniemnto (standard) /// public int tOff = 22; /// /// Parametri simulazione oscillazione bit 2 /// protected simPar bit2; /// /// Parametri simulazione oscillazione bit 3 /// protected simPar bit3; /// /// Parametri simulazione oscillazione bit 4 /// protected simPar bit4; /// /// Parametri simulazione oscillazione bit 5 /// protected simPar bit5; /// /// ultimo controllo decremento eventi /// protected DateTime lastEvCheck; /// /// Durata minima ODL x reset quando pezzi iob > pezzi macchina... /// protected int minDurataODL = 480; /// /// variabile di appoggio x stato segnale contapezzo /// protected bool sigPzCount = false; /// /// Tempo di MINIMO attesa x simulazione parametri /// protected int waitSimPar = utils.CRI("waitSimPar"); /// /// Ultimo istante in cui sono stati generati dati di simulazione /// protected DateTime lastSimData; /// /// estende l'init della classe base... /// /// /// public IobSimula(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf) { // gestione invio ritardato contapezzi pzCountDelay = utils.CRI("pzCountDelay"); DateTime adesso = DateTime.Now; lastPzCountSend = adesso; lastWarnODL = adesso; lastEvCheck = adesso; lastSimData = adesso; // sistemo parametri x simulazione... if (cIobConf.optPar.Count > 0) { if (!string.IsNullOrEmpty(getOptPar("PER_BASE"))) { int.TryParse(getOptPar("PER_BASE"), out periodoMSec); // aggiungo NOISE... +/- 20% Random rnd = new Random(); int noise = rnd.Next(1, periodoMSec / 5); periodoMSec += noise - (periodoMSec / 10); } simPowerOnOff = false; bool.TryParse(getOptPar("SIM_POW_ON_OFF"), out simPowerOnOff); int.TryParse(getOptPar("T_ON"), out tOn); int.TryParse(getOptPar("T_OFF"), out tOff); bit2 = setupSimPar("SIM_PZCNT"); bit3 = setupSimPar("SIM_ALARM"); bit4 = setupSimPar("SIM_MANU"); bit5 = setupSimPar("SIM_SLOW"); int.TryParse(getOptPar("MIN_DURATA_ODL"), out minDurataODL); } setParamPlc(); // ricarico da server i dati dei pezzi fatti... lgInfo("Init contapezzi SIMULA: pzCntReload(true)"); pzCntReload(true); // imposto pezzi CNC ai pezzi contati da server... lastCountCNC = contapezzi; lgInfo($"Impostazione iniziale contatori: contapezzi macchina lastCountCNC: {lastCountCNC} | contapezzi: {contapezzi}"); } /// /// Effettua reset del contapezzi /// /// public override bool resetContapezziCNC() { bool answ = false; // ...SE abilitato da conf IOB if (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET") == "TRUE") { // fingo di aver fatto... answ = true; } return answ; } /// /// Setup aprametri di simulazione per BIT indicato /// /// private simPar setupSimPar(string keyName) { simPar answ = new simPar(); if (cIobConf.optPar.Count > 0) { if (cIobConf.optPar.ContainsKey(keyName)) { string fullVal = getOptPar(keyName); if (!string.IsNullOrEmpty(fullVal) && fullVal.IndexOf("|") > 0) { string[] param = fullVal.Split('|'); int.TryParse(param[0], out answ.wait); int.TryParse(param[1], out answ.duration); // aggiongo noise, +/- 40%... Random rnd = new Random(); int noise = rnd.Next(1, answ.wait * 40 / 100); answ.wait += noise - (answ.wait * 20 / 100); } } } return answ; } #if false /// /// Setup singolo parametro /// /// /// private int setIntSimPar(string keyName) { int answ = 1; int.TryParse(getOptPar(keyName), out answ); // aggiongo noise, +/- 20%... Random rnd = new Random(); int noise = rnd.Next(1, answ / 5); answ += noise - (answ / 10); return answ; } #endif public override void tryConnect() { base.tryConnect(); connectionOk = true; } public override void tryDisconnect() { base.tryDisconnect(); connectionOk = false; } #region Metodi specifici (da verificare/completare in implementazione) /// /// Effettua vero processing contapezzi /// public override void processContapezzi() { } /// /// Effettua lettura semafori principale /// Parametri da aggiornare x display in form /// public override void readSemafori(ref newDisplayData currDispData) { base.readSemafori(ref currDispData); // decodifica e gestione decodeToBaseBitmap(); decodeOtherData(); reportRawInput(ref currDispData); } /// /// Processo contatori eventi... /// public override void processVHF() { if (lastEvCheck.AddMilliseconds(periodoMSec) < DateTime.Now) { // decremento contatore ultimo evento bit2.wait--; bit3.wait--; bit4.wait--; bit5.wait--; lastEvCheck = DateTime.Now; } } /// /// Effettua decodifica aree memoria alla bitmap usata x MAPO /// private void decodeToBaseBitmap() { // init a zero... B_input = 0; bool sendContapezzi = false; /* ----------------------------------------------------- * bitmap MAPO * B0: POWER_ON * B1: RUN * B2: pzCount * B3: allarme * B4: manuale * B5: emergenza - non usato x ora * B6: pallet 1 (SE doppio pallet) * B7: pallet 2 (SE doppio pallet) ----------------------------------------------------- */ // di base macchina in RUN B_input = 3; /*---------------------------------------- * Simulazione segnali con priorità: * - Power ON / OFF (bit0/1) * - ALLARMI * - SLOW * - MANUALE * - contapezzi * *----------------------------------------*/ // se simulo PowerOn/Off --> spegnimento con CoolDown e accensione con WarmUp.. if (simPowerOnOff) { DateTime adesso = DateTime.Now; // se l'orario è dopo le tOff (tipicamente 22) --> NO RUN... if (adesso.Hour >= tOff || adesso.Hour <= tOn) { // se prima/ultima mezz'ora è ancora accesa NON in run... if (adesso.AddMinutes(-30).Hour < tOff || adesso.AddMinutes(30).Hour > tOn) { B_input = 1; } else { B_input = 0; } } } // in primis verifico SE posso inviare in blocco i pezzi...... SE MP online e SE NON E' MULTI if (MPOnline && !isMulti) { // SE IOB online... if (IobOnline) { // se il contapezzi è OLTRE il valore inviato.... if (lastCountCNC > contapezzi) { // invio SOLO SE sono OLTRE i numSim pz e li invio TUTTI in blocco if ((lastCountCNC - contapezzi) > minSendPzCountBlock) { trySendPzCountBlock(); sigPzCount = false; } // altrimenti invio 1 segnale else { // se NON STAVA inviando di già... if (!sigPzCount) { // segnalo BIT (1 pz) B_input += (1 << 2); sigPzCount = true; contapezzi++; // invio conferma contapezzi.. string retVal = utils.callUrl($"{urlSetPzCount}{contapezzi}"); } else { string retVal = utils.callUrl($"{urlSetPzCount}{contapezzi}"); sigPzCount = false; } } lgInfo($"S01: Valori contatori: contapezzi macchina lastCountCNC: {lastCountCNC} | contapezzi: {contapezzi} | lastCountCNC > contapezzi"); } } } // questa parte la processo SOLO SE sono in run --> B_input == 3 if (B_input == 3) { if (bit3.wait <= 0) { // segnalo BIT B_input += (1 << 3); // decremento duration bit3.duration--; // controllo se sia scaduta la duration... in quel caso reset... if (bit3.duration <= 0) { bit3 = setupSimPar("SIM_ALARM"); } } else if (bit4.wait <= 0) { // segnalo BIT B_input += (1 << 4); // decremento duration bit4.duration--; // controllo se sia scaduta la duration... in quel caso reset... if (bit4.duration <= 0) { bit4 = setupSimPar("SIM_MANU"); } // in manuale: provo split ODL trySplitOdl(); } else if (bit5.wait <= 0) { // segnalo BIT B_input += (1 << 5); // decremento duration bit5.duration--; // controllo se sia scaduta la duration... in quel caso reset... if (bit5.duration <= 0) { bit5 = setupSimPar("SIM_SLOW"); } } else if (bit2.wait <= 0) { // salvo nuovo contapezziPLC (incremento RAND 0..5) var rand = new Random(); // se online vero delta altrimenti solo 0..1 50% probabilità int delta = IobOnline ? rand.Next(1, 5) : rand.Next(0, 2); if (!isMulti) { // solo se MP online... if (MPOnline) { lastCountCNC += delta; lgInfo($"S01: Valori contatori: contapezzi macchina lastCountCNC: {lastCountCNC} | contapezzi: {contapezzi} | aggiunto delta {delta}"); } } // SOLO SE sono online... if (IobOnline) { // se multipallet --> cP a zero! if (isMulti) { cP = 0; } // se NON Multi fa contapezzi... else { // SE NON SONO GIA' OLTRE il contapezzi if (contapezzi < lastCountCNC) { // segnalo BIT (1 pz) B_input += (1 << 2); } } // decremento duration bit2.duration--; // controllo se sia scaduta la duration... in quel caso reset... if (bit2.duration <= 0) { bit2 = setupSimPar("SIM_PZCNT"); sendContapezzi = true; // registro contapezzi contapezzi++; lgInfo($"S01: Valori contatori: contapezzi macchina lastCountCNC: {lastCountCNC} | contapezzi: {contapezzi} - incremento contapezzi per bit2.duration <= 0"); } if (sendContapezzi) { // controllo se ALMENO sia pingabile il server if (checkServerAlive) { // invio a server contapezzi (aggiornato) string retVal = utils.callUrl(urlSetPzCount + contapezzi.ToString()); // verifica se tutto OK if (retVal != "OK") { // errore salvataggio contapezzi lgInfo(string.Format("Errore salvataggio Contapezzi SIMULAZIONE {0} | Errore salvataggio: {1}", contapezzi, retVal)); } // resetto timer... lastPzCountSend = DateTime.Now; } } // provo a fare split ODL SE NON E' multi.... trySplitOdl(); } } // se multi gestisco il bit delle tavole... if (isMulti) { // se sono in fase di fronte d'uscita (invio contapezzi) INVERTO nP... if (sendContapezzi) { nP = nP == 1 ? 2 : 1; // assegno a cP il valore nP... cP = nP; lastCountCNC++; } // se cP > 0 --> segnalo bit tavola... if (cP == 1) { B_input += (1 << 6); } else if (cP == 2) { B_input += (1 << 7); } } // init obj display newDisplayData currDispData = new newDisplayData(); currDispData.counter = contapezzi; currDispData.semOut = Semaforo.SV; raiseRefresh(currDispData); } } /// /// provo a chaimare split ODL /// private void trySplitOdl() { if (!isMulti) { // solo se ODL è in lavorazione da ALMENO minDurataODL minuti... DateTime inizioOdl = DateTime.Now.AddDays(-1); string rawDataInizio = callUrl(urlInizioOdlIob, false); DateTime.TryParse(rawDataInizio, out inizioOdl); if (DateTime.Now.Subtract(inizioOdl).TotalMinutes > minDurataODL) { // invio reset ODL... forceSplitOdl(); } } } /// /// Decodifica il resto dell'area x i dati accessori (allarmi, ...) /// private void decodeOtherData() { } /// /// Recupero programma in lavorazione /// /// public override string getPrgName() { // valore non presente in vers default... se gestito fare override string prgName = string.Format("DEMO_{0:00}", DateTime.Now.Minute); return prgName; } #if false /// /// Genera un valore random POSSIBILMENTE impiegando i valori min/max da conf memoria /// /// /// protected string getSimVal(string memName) { Random rnd = new Random(); int answ = 0; int minVal = 0; int maxVal = 100; if (memMap.mMapRead.ContainsKey(memName)) { minVal = memMap.mMapRead[memName].minVal; maxVal = memMap.mMapRead[memName].maxVal; } answ = rnd.Next(minVal, maxVal); return answ.ToString(); } #endif /// /// Recupero info sistema generiche /// public override Dictionary getSysInfo() { // valore non presente in vers default... se gestito fare override Dictionary outVal = new Dictionary(); outVal.Add("MACHINE", "IOB_SIM"); return outVal; } /// /// Recupero dati dinamici... /// public override Dictionary getDynData() { // valore non presente in vers default... se gestito fare override Dictionary outVal = new Dictionary(); // verificare periodo SIM parametri... se passato li invio altrimenti NO... FIX a 20 sec if (lastSimData.AddSeconds(waitSimPar) < DateTime.Now) { Random rnd = new Random(); // controllo conf memorie json (se ci sono...) try { if (memMap.mMapWrite.Count > 0) { foreach (var item in memMap.mMapWrite) { outVal.Add(item.Key, item.Value.value); } } if (memMap.mMapRead.Count > 0) { foreach (var item in memMap.mMapRead) { // uso factor come valore MAX ammesso int randVal = rnd.Next(item.Value.minVal, item.Value.maxVal); outVal.Add(item.Key, randVal.ToString()); } } } catch { } lastSimData = DateTime.Now; } return outVal; } /// /// Recupera e processa allarmi CNC... /// public override Dictionary getCncAlarms() { Dictionary outVal = new Dictionary(); return outVal; } #endregion } }