609 lines
19 KiB
C#
609 lines
19 KiB
C#
using IOB_UT;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace IOB_WIN
|
|
{
|
|
/// <summary>
|
|
/// Configuraizone eventi da simulare
|
|
/// </summary>
|
|
public class simPar
|
|
{
|
|
/// <summary>
|
|
/// Attesa per evento
|
|
/// </summary>
|
|
public int wait = 10;
|
|
/// <summary>
|
|
/// Durata dell'evento
|
|
/// </summary>
|
|
public int duration = 1;
|
|
/// <summary>
|
|
/// DateTime ultimo evento
|
|
/// </summary>
|
|
public DateTime lastEv = DateTime.Now;
|
|
}
|
|
|
|
public class IobSimula : IobGeneric
|
|
{
|
|
/// <summary>
|
|
/// pallet corrente
|
|
/// </summary>
|
|
protected int cP = 1;
|
|
/// <summary>
|
|
/// pallet successivo (next)
|
|
/// </summary>
|
|
protected int nP = 1;
|
|
/// <summary>
|
|
/// periodo base del simulatore (in secondi)
|
|
/// </summary>
|
|
protected int periodoMSec = 1000;
|
|
/// <summary>
|
|
/// BOOL: indica se simulare powerOn/Off (bit 0 e 1) compresi WarmUp e CoolDown
|
|
/// </summary>
|
|
protected bool simPowerOnOff;
|
|
/// <summary>
|
|
/// Ora dia ccensione (standard)
|
|
/// </summary>
|
|
public int tOn = 6;
|
|
/// <summary>
|
|
/// Ora spegniemnto (standard)
|
|
/// </summary>
|
|
public int tOff = 22;
|
|
/// <summary>
|
|
/// Parametri simulazione oscillazione bit 2
|
|
/// </summary>
|
|
protected simPar bit2;
|
|
/// <summary>
|
|
/// Parametri simulazione oscillazione bit 3
|
|
/// </summary>
|
|
protected simPar bit3;
|
|
/// <summary>
|
|
/// Parametri simulazione oscillazione bit 4
|
|
/// </summary>
|
|
protected simPar bit4;
|
|
/// <summary>
|
|
/// Parametri simulazione oscillazione bit 5
|
|
/// </summary>
|
|
protected simPar bit5;
|
|
/// <summary>
|
|
/// ultimo controllo decremento eventi
|
|
/// </summary>
|
|
protected DateTime lastEvCheck;
|
|
/// <summary>
|
|
/// estende l'init della classe base...
|
|
/// </summary>
|
|
/// <param name="caller"></param>
|
|
/// <param name="adpConf"></param>
|
|
public IobSimula(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf)
|
|
{
|
|
// gestione invio ritardato contapezzi
|
|
pzCountDelay = utils.CRI("pzCountDelay");
|
|
lastPzCountSend = DateTime.Now;
|
|
lastWarnODL = DateTime.Now;
|
|
lastEvCheck = DateTime.Now;
|
|
// sistemo parametri x simulazione...
|
|
if (cIobConf.optPar.Count > 0)
|
|
{
|
|
if (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");
|
|
}
|
|
setParamPlc();
|
|
}
|
|
public override void processAutoOdl()
|
|
{
|
|
bool fatto = false;
|
|
if (getOptPar("AUTO_CHANGE_ODL") != "")
|
|
{
|
|
string IOB_MULTI_CNAME = "";
|
|
string[] elencoMulti = null;
|
|
string fullUrl = "";
|
|
if (isMulti)
|
|
{
|
|
// devo chiamare cambio ODL x OGNI tavola: mi servono i parametri opzionali...
|
|
IOB_MULTI_CNAME = getOptPar("IOB_MULTI_CNAME");
|
|
elencoMulti = IOB_MULTI_CNAME.Split(',');
|
|
}
|
|
// controllo SIA abilitato...
|
|
bool doProc = false;
|
|
DateTime adesso = DateTime.Now;
|
|
bool.TryParse(getOptPar("AUTO_CHANGE_ODL"), out doProc);
|
|
if (doProc)
|
|
{
|
|
// carico i parametri di configurazione x reset ODL...
|
|
string CHANGE_ODL_HOURS = getOptPar("CHANGE_ODL_IDLE_MIN");
|
|
string CHANGE_ODL_IDLE_MIN = getOptPar("CHANGE_ODL_IDLE_MIN");
|
|
if (CHANGE_ODL_HOURS != "" && CHANGE_ODL_IDLE_MIN != "")
|
|
{
|
|
int minOdlDurHours = -1;
|
|
int minPlcIdelMin = -1;
|
|
int.TryParse(CHANGE_ODL_HOURS, out minOdlDurHours);
|
|
int.TryParse(CHANGE_ODL_IDLE_MIN, out minPlcIdelMin);
|
|
// controllo parametri validi
|
|
if (minOdlDurHours > 0 && minPlcIdelMin > 0)
|
|
{
|
|
// leggo da server inizio ODL... se non multi 1 solo...
|
|
DateTime inizioOdl = DateTime.Now;
|
|
string rawDataInizio = "";
|
|
if (!isMulti)
|
|
{
|
|
rawDataInizio = callUrl(urlInizioOdlIob, true);
|
|
DateTime.TryParse(rawDataInizio, out inizioOdl);
|
|
}
|
|
else
|
|
{
|
|
DateTime tmpData = DateTime.Now;
|
|
// prendo il + vecchio...
|
|
foreach (var item in elencoMulti)
|
|
{
|
|
fullUrl = $"{urlInizioOdlIob}|{item}";
|
|
rawDataInizio = callUrl(fullUrl, true);
|
|
DateTime.TryParse(rawDataInizio, out tmpData);
|
|
inizioOdl = (tmpData < inizioOdl) ? tmpData : inizioOdl;
|
|
}
|
|
}
|
|
//verifico se sia scaduto...
|
|
if (inizioOdl.AddHours(minOdlDurHours) < adesso)
|
|
{
|
|
string rawIdle = "";
|
|
int idlePeriod = 0;
|
|
if (!isMulti)
|
|
{
|
|
// controllo SE sono fermo (spento o in manuale) per il periodo minimo richiesto...
|
|
rawIdle = callUrl(urlIdleTime, true);
|
|
int.TryParse(rawIdle, out idlePeriod);
|
|
}
|
|
else
|
|
{
|
|
int tmpIdle = 0;
|
|
// prendo il + grande...
|
|
foreach (var item in elencoMulti)
|
|
{
|
|
fullUrl = $"{urlIdleTime}|{item}";
|
|
rawIdle = callUrl(fullUrl, true);
|
|
int.TryParse(rawIdle, out tmpIdle);
|
|
idlePeriod = tmpIdle > idlePeriod ? tmpIdle : idlePeriod;
|
|
}
|
|
}
|
|
if (idlePeriod >= minPlcIdelMin)
|
|
{
|
|
/***************************************************
|
|
* Descrizione procedura (OK X SIMULATORI...)
|
|
*
|
|
* - chiamata su MP/IO
|
|
* - verifica che su DB sia abilitato AUTO ODL
|
|
* - il server inserisce un evento fine prod HW 1 minuto prima e inizio setup HW
|
|
* - viene duplicato e chiuso ODL corrente
|
|
* - viene fatto partire ODL nuovo ADESSO
|
|
* - num pezzi come ODL precedente (o da media 3 ODL precedenti)
|
|
* - conferme pezzi & co... gestione NULL (NON SERVONO si tratta di impianti SENZA gestione vera ODL)
|
|
* - reset contapezzi PLC locale...
|
|
*
|
|
*
|
|
*
|
|
* DA VALUTARE (x macchine tipo linea con + impianti... es valvital) SE
|
|
* - creare una gestione ALTERNATIVA sul server che preveda impianto LEADER e impianti follower (RIGIDAMENTE CONFIGURATI)
|
|
* - ogni volta che si fa setup LEADER --> si ripete su impianti FOLLOWER (eventi!!!)
|
|
* - viene fatto reset contapezzi sui follower (+ altre operazioni opzionali, ES imposstazione nome commessa, quantità, articolo...)
|
|
* - viene fatto reset + nuovo ODL (con stessi articoli e quantità) su follower, SENZA avere gestione x ODL di un codice esterno (quindi registra TUTTO ma NON RITORNERA' dati non avendo link verso esterno)
|
|
* - serve NUOVA TABELLA delle macchine LEADER | FOLLOWER (1:n) e gestione da MP/IO
|
|
*
|
|
***************************************************/
|
|
|
|
string rawSplit = "";
|
|
// se multi gestisco il bit delle tavole...
|
|
if (!isMulti)
|
|
{
|
|
// invio chiamata URL x reset ODL su macchina
|
|
rawSplit = callUrl(urlForceSplit, false);
|
|
fatto = (rawSplit == "OK") ? true : false;
|
|
}
|
|
else
|
|
{
|
|
foreach (string item in elencoMulti)
|
|
{
|
|
// invio chiamata URL x reset ODL su macchina, ATTENZIONE scriviamo | al posto di "#" che in URL sarebbe filtrato...
|
|
fullUrl = $"{urlForceSplit}|{item}";
|
|
rawSplit = callUrl(fullUrl, true);
|
|
}
|
|
fatto = (rawSplit == "OK") ? true : false;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// loggo se fatto
|
|
if (fatto)
|
|
{
|
|
lg.Info($"Effettuato processAutoOdl");
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Effettua reset del contapezzi
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
/// <summary>
|
|
/// Setup aprametri di simulazione per BIT indicato
|
|
/// </summary>
|
|
/// <param name="keyName"></param>
|
|
private simPar setupSimPar(string keyName)
|
|
{
|
|
simPar answ = new simPar();
|
|
if (cIobConf.optPar.Count > 0)
|
|
{
|
|
if (cIobConf.optPar.ContainsKey(keyName))
|
|
{
|
|
string fullVal = getOptPar(keyName);
|
|
if (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;
|
|
}
|
|
/// <summary>
|
|
/// Setup singolo parametro
|
|
/// </summary>
|
|
/// <param name="keyName"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
/// <summary>
|
|
/// Verifica se sia machcina multi = DoppioPallet da CONF
|
|
/// </summary>
|
|
public bool isMulti
|
|
{
|
|
get
|
|
{
|
|
bool answ = false;
|
|
string keyName = "SIM_MULTI";
|
|
if (cIobConf.optPar.Count > 0)
|
|
{
|
|
if (cIobConf.optPar.ContainsKey(keyName))
|
|
{
|
|
string SIM_MULTI = getOptPar(keyName);
|
|
answ = SIM_MULTI == "1";
|
|
}
|
|
}
|
|
return answ;
|
|
}
|
|
}
|
|
|
|
public override void tryConnect()
|
|
{
|
|
base.tryConnect();
|
|
connectionOk = true;
|
|
}
|
|
|
|
public override void tryDisconnect()
|
|
{
|
|
base.tryDisconnect();
|
|
connectionOk = false;
|
|
}
|
|
|
|
#region Metodi specifici (da verificare/completare in implementazione)
|
|
|
|
/// <summary>
|
|
/// Effettua vero processing contapezzi
|
|
/// </summary>
|
|
public override void processContapezzi()
|
|
{
|
|
}
|
|
/// <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);
|
|
// decodifica e gestione
|
|
decodeToBaseBitmap();
|
|
decodeOtherData();
|
|
reportRawInput(ref currDispData);
|
|
}
|
|
/// <summary>
|
|
/// Processo contatori eventi...
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// questa aprte 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");
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
// se multipallet --> cP a zero!
|
|
if (isMulti)
|
|
{
|
|
cP = 0;
|
|
}
|
|
// se NON Multi fa contapezzi...
|
|
else
|
|
{
|
|
// segnalo BIT
|
|
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");
|
|
// salvo nuovo contapezzi (incremento di 1...)
|
|
contapezzi++;
|
|
sendContapezzi = true;
|
|
// registro contapezzi
|
|
lgInfo(string.Format("Contapezzi SIMULAZIONE: {0}", contapezzi));
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
// se cP > 0 --> segnalo bit tavola...
|
|
if (cP == 1)
|
|
{
|
|
B_input += (1 << 6);
|
|
}
|
|
else if (cP == 2)
|
|
{
|
|
B_input += (1 << 7);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decodifica il resto dell'area x i dati accessori (allarmi, ...)
|
|
/// </summary>
|
|
private void decodeOtherData()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recupero programma in lavorazione
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
/// <summary>
|
|
/// Recupero dati override (da area G che è già stata letta...)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override Dictionary<string, string> getOverrides()
|
|
{
|
|
Dictionary<string, string> outVal = new Dictionary<string, string>();
|
|
// processo SOLO SE connected...
|
|
if (connectionOk)
|
|
{
|
|
Random rnd = new Random();
|
|
int feedOvr = rnd.Next(1, 100);
|
|
int rapdOvr = rnd.Next(1, 120);
|
|
outVal.Add("FEED_OVER", feedOvr.ToString());
|
|
outVal.Add("RAPID_OVER", rapdOvr.ToString());
|
|
|
|
}
|
|
return outVal;
|
|
}
|
|
/// <summary>
|
|
/// Recupero info sistema generiche
|
|
/// <returns></returns>
|
|
public override Dictionary<string, string> getSysInfo()
|
|
{
|
|
// valore non presente in vers default... se gestito fare override
|
|
Dictionary<string, string> outVal = new Dictionary<string, string>();
|
|
outVal.Add("MACHINE", "IOB_SIM");
|
|
return outVal;
|
|
}
|
|
/// <summary>
|
|
/// Recupero dati dinamici...
|
|
/// </summary>
|
|
public override Dictionary<string, string> getDynData()
|
|
{
|
|
// valore non presente in vers default... se gestito fare override
|
|
Dictionary<string, string> outVal = new Dictionary<string, string>();
|
|
Random rnd = new Random();
|
|
int posX = rnd.Next(1, 1000);
|
|
int posY = rnd.Next(1, 1000);
|
|
int posZ = rnd.Next(1, 1000);
|
|
outVal.Add("POS_X", posX.ToString());
|
|
outVal.Add("POS_Y", posY.ToString());
|
|
outVal.Add("POS_Z", posZ.ToString());
|
|
// controllo conf memorie json (se ci sono...)
|
|
try
|
|
{
|
|
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
|
|
{ }
|
|
return outVal;
|
|
}
|
|
/// <summary>
|
|
/// Recupera e processa allarmi CNC...
|
|
/// </summary>
|
|
public override Dictionary<string, string> getCncAlarms()
|
|
{
|
|
Dictionary<string, string> outVal = new Dictionary<string, string>();
|
|
return outVal;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|