Files
Mapo-IOB-WIN/IOB-WIN-NEXT/IobSimula.cs
T
2022-10-06 12:19:47 +02:00

1286 lines
50 KiB
C#

using EgwProxy.Ftp;
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading.Tasks;
using static System.Net.WebRequestMethods;
namespace IOB_WIN_NEXT
{
public class IobSimula : IobGeneric
{
#region Public Fields
/// <summary>
/// Ora spegniemnto (standard)
/// </summary>
public int tOff = 22;
/// <summary>
/// Ora dia ccensione (standard)
/// </summary>
public int tOn = 6;
#endregion Public Fields
#region Public Constructors
/// <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");
DateTime adesso = DateTime.Now;
lastPzCountSend = adesso;
lastWarnODL = adesso;
lastEvCheck = adesso;
lastSimData = adesso;
lastRedDuration = adesso;
// setup allarmi simulati
for (int i = 1; i <= 8; i++)
{
alarmMessages.Add($"Allarme {i:000}");
}
// sistemo parametri x simulazione...
if (cIobConf.optPar.Count > 0)
{
if (!string.IsNullOrEmpty(getOptPar("PER_BASE")))
{
int.TryParse(getOptPar("PER_BASE"), out periodoMSec);
// aggiungo NOISE... +/- 10%
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");
bit6 = setupSimPar("SIM_WUCD");
bit7 = setupSimPar("SIM_EMRG");
int.TryParse(getOptPar("MIN_DURATA_ODL"), out minDurataODL);
var strDisableSim = getOptPar("DISABLE_SIM_STATUS");
if (!string.IsNullOrEmpty(strDisableSim))
{
bool.TryParse(strDisableSim, out disableSimStatus);
}
// simulazioni azioni utente
simRC = setupSimPar("SIM_RC");
simRS = setupSimPar("SIM_RS");
simDich = setupSimPar("SIM_DICH");
int.TryParse(getOptPar("SIM_MATR_OPR"), out matrOpr);
}
setParamPlc();
// ricarico da server i dati dei pezzi fatti...
lgInfo("Init contapezzi SIMULA: pzCntReload(true)");
if (!isMulti)
{
pzCntReload(true);
}
// imposto pezzi CNC ai pezzi contati da server...
contapezziPLC = contapezziIOB;
lgInfo($"Impostazione iniziale contatori: contapezzi macchina contapezziPLC: {contapezziPLC} | contapezziIOB: {contapezziIOB}");
// parto processando dynData
processDynData();
// simulazione processo FTP (Cimolai) - FIXME TODO DeleteME
simulaFTP();
}
private void simulaFTP()
{
// fixme todo: test scrittura file csv...
var taskGet = iobGetDataFromServer();
var taskWrite = iobWriteLocalCSV();
var taskSend = iobSendFTP("");
}
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
protected class ArtRow
{
#region Public Properties
[Description("Matricola_articolo")]
public string Matricola { get; set; } = "";
[Description("Articolo")]
//[Display(Name = "Articolo", Order = 1)]
public string Articolo { get; set; } = "";
[Description("Identificazione")]
//[Display(Name = "Identificazione", Order = 2)]
public string Descrizione { get; set; } = "";
[Description("Peso_teorico_linea1[ton]")]
public int Peso_01 { get; set; } = 0;
[Description("Peso_teorico_linea2[ton]")]
public int Peso_02 { get; set; } = 0;
[Description("Peso_teorico_linea3[ton]")]
public int Peso_03 { get; set; } = 0;
[Description("Peso_teorico_linea4[ton]")]
public int Peso_04 { get; set; } = 0;
[Description("Pos. Carrello 1[mm]")]
public int PosizCarrello_01 { get; set; } = 0;
[Description("Pos. Carrello 2[mm]")]
public int PosizCarrello_02 { get; set; } = 0;
[Description("Pos. Carrello 3[mm]")]
public int PosizCarrello_03 { get; set; } = 0;
[Description("Pos. Carrello 4[mm]")]
public int PosizCarrello_04 { get; set; } = 0;
[Description("Limitazione_velocita_sollevamento[%]")]
//[Display(Name = "Limitazione_velocita_sollevamento[%]", Order = 3)]
public int LimiteVel { get; set; } = 100;
#endregion Public Properties
}
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
protected class JobRow
{
#region Public Properties
[Description("Matricola")]
public string Matricola { get; set; } = "";
[Description("Commessa")]
public string Commessa { get; set; } = "";
[Description("Articolo")]
public string Articolo { get; set; } = "";
[Description("Identificazione")]
public string Descrizione { get; set; } = "";
[Description("Data inserimento")]
public string DataIns { get; set; } = "";
[Description("Ora inserimento")]
public string OraIns { get; set; } = "";
[Description("Lavorazione")]
public string Lavorazione { get; set; } = "";
#endregion Public Properties
}
protected List<ArtRow> ListaArticoli { get; set; } = new List<ArtRow>();
protected List<JobRow> ListaJobs { get; set; } = new List<JobRow>();
/// <summary>
/// Recupera da server set di dati specifici x IOB : qui per compilare files CSV
/// </summary>
/// <returns></returns>
protected override bool iobGetDataFromServer()
{
bool answ = false;
// recupera dati da server tramite chiamate REST a MP/IO/IOB...
// FIXME TODO !!!
Random rnd = new Random();
int firstId = rnd.Next(1000);
simArticoli(firstId);
simJobs(firstId);
answ = true;
return answ;
}
private void simArticoli(int firstId)
{
// FAKE: crea finti dati...
Random rnd = new Random();
ListaArticoli = new List<ArtRow>();
for (int i = firstId; i <= firstId + 5; i++)
{
ArtRow artRow = new ArtRow()
{
Matricola = $"{i}",
Articolo = $"Articolo_{i:000}",
Descrizione = $"Descrizione_{i:000}",
Peso_01 = rnd.Next(100),
Peso_02 = rnd.Next(100),
Peso_03 = rnd.Next(100),
Peso_04 = rnd.Next(100),
PosizCarrello_01 = rnd.Next(0, 2000),
PosizCarrello_02 = rnd.Next(2100, 5000),
PosizCarrello_03 = rnd.Next(6000, 15000),
PosizCarrello_04 = rnd.Next(16000, 25000)
};
ListaArticoli.Add(artRow);
}
}
private void simJobs(int firstId)
{
// FAKE: crea finti dati...
Random rnd = new Random();
ListaJobs = new List<JobRow>();
for (int i = firstId; i <= firstId + 5; i++)
{
DateTime adesso = DateTime.Now;
JobRow jobRow = new JobRow()
{
Matricola = $"{i}",
Commessa = $"ODL000{i:0000}",
Articolo = $"Articolo_{i:000}",
Descrizione = $"Descrizione_{i:000}",
DataIns = $"{adesso:dd/MM/yyyy}",
OraIns = $"{adesso.AddMinutes(-i):HH:mm}",
Lavorazione = "TEST LAV"
};
ListaJobs.Add(jobRow);
}
}
/// <summary>
/// Effettua upload verso server FTP della macchina dei files nella folder indicata
/// </summary>
/// <param name="folderDir"></param>
/// <returns></returns>
protected override bool iobSendFTP(string folderDir)
{
bool answ = false;
var ftpClient = new Manager("ftp.steamware.net", "testftpuser", "we4reFromB3rghem!", "", true);
var testServer = ftpClient.serverOk();
if (testServer)
{
var srvType = ftpClient.serverType();
string remDir = "data/test_directory";
var preTest = ftpClient.dirExists(remDir);
if (!preTest)
{
var dirCreate = ftpClient.createDir(remDir);
}
string basePath = Directory.GetCurrentDirectory();
string localPath = Path.Combine(basePath, "temp/");
var dirUploaded = ftpClient.sendDir(localPath, remDir);
// se ok --> sposto invio
DateTime adesso = DateTime.Now;
string archPath = Path.Combine(basePath, "DATA", "HIST", $"{adesso:yyyy}");
if (!Directory.Exists(archPath))
{
Directory.CreateDirectory(archPath);
}
Directory.Move(localPath, Path.Combine(archPath, $"{adesso:MMddHHmmss}"));
}
return answ;
}
/// <summary>
/// Prepara files CSV da inviare alla macchina
/// </summary>
protected override bool iobWriteLocalCSV()
{
bool answ = false;
// salvo articoli
string basePath = Directory.GetCurrentDirectory();
string tempDir = Path.Combine(basePath, "temp");
if (!Directory.Exists(tempDir))
{
Directory.CreateDirectory(tempDir);
}
string filePath = Path.Combine(tempDir, "articoli.csv");
answ = DataExport.SaveToCsv(ListaArticoli, filePath);
if (answ)
{
// salvo PODL
filePath = Path.Combine(tempDir, $"{DateTime.Now:dd-MM-yyyy}.csv");
answ = DataExport.SaveToCsv(ListaJobs, filePath);
}
return answ;
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 1:1 (in realtà SOLO forceSetPzCount x ora)
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// Verificare il protocollo: dovrebeb togliere SOLO i task eseguiti...
Dictionary<string, string> taskDone = new Dictionary<string, string>();
// controlo SE sia disabilitata simulazione principale
if (!disableSimStatus)
{
string taskVal = "";
// verifico non sia null
if (task2exe != null)
{
// cerco task specifici
foreach (var item in task2exe)
{
taskVal = "";
// converto richiesta in enum...
taskType tName = taskType.nihil;
Enum.TryParse(item.Key, out tName);
// controllo sulla KEY
switch (tName)
{
case taskType.setArt:
case taskType.setComm:
case taskType.setProg:
case taskType.setPzComm:
memMap.mMapWrite[item.Key].value = item.Value;
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | UPDATED memMap.mMapWrite";
break;
case taskType.setParameter:
// richiedo da URL i parametri WRITE da popolare
lgInfo("Chiamata processMemWriteRequests");
taskVal = processMemWriteRequests();
// se restituiscce "" faccio altra prova...
if (string.IsNullOrEmpty(taskVal))
{
// i parametri me li aspetto come stringa composta paramName|paramvalue
if (item.Value.Contains("|"))
{
string[] paramsJob = item.Value.Split('|');
taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}";
}
else
{
taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value";
}
}
break;
case taskType.nihil:
case taskType.fixStopSetup:
case taskType.forceResetPzCount:
case taskType.sendWatchDogMes2Plc:
case taskType.startSetup:
case taskType.stopSetup:
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
break;
case taskType.forceSetPzCount:
// forzo sul SIM il valore pzCount dell'IOB...
int newPzCount = contapezziPLC;
bool fatto = int.TryParse(item.Value, out newPzCount);
if (fatto)
{
// verifico SE sia ammesso il cambio ...
int deltaPzCount = newPzCount - contapezziPLC;
double maxDelta = DateTime.Now.Subtract(plcLastPzRead).TotalMinutes / (plcAvgTc / 60);
// se incremento superiore del doppio atteso --> segnalo errore
// e NON accetto
if (deltaPzCount > (maxDelta * maxPzDeltaPerc) / 100)
{
lgError($"[DELTA CHECK]: intremento contapezziPLC troppo elevato: lettura {newPzCount} | contapezzi attuale: {contapezziPLC} | ultima lettura PLC: {plcLastPzRead} | TCiclo medio: {plcAvgTc}s | incremento accettato ");
}
else
{
contapezziPLC = newPzCount;
taskVal = $"Set new contapezziPLC: {contapezziPLC}";
}
}
break;
default:
taskVal = "SKIPPED | NO EXEC";
break;
}
// aggiungo task!
taskDone.Add(item.Key, taskVal);
}
}
}
return taskDone;
}
/// <summary>
/// Recupera e processa allarmi CNC...
/// </summary>
public override Dictionary<string, string> getCncAlarms()
{
Dictionary<string, string> outVal = new Dictionary<string, string>();
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>();
// 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)
{
// se il TIPO di valore è livello --> simulo variazione da MAX --> min,
// con un delta in CALO pari a factor * (80-120)%
if (item.Value.name == "SIM_LEVEL")
{
// verifico last value
float lastVal = 0;
float.TryParse(item.Value.value, out lastVal);
if (lastVal == 0)
{
lastVal = item.Value.maxVal - (float)item.Value.factor;
}
// decremento casuale...
float newVal = lastVal - ((float)item.Value.factor * rnd.Next(40, 120) / 100);
// se inferiore a minimo --> massimo!
if (newVal < item.Value.minVal)
{
newVal = item.Value.maxVal;
}
// salvo il suo VALUE...
item.Value.value = $"{newVal}";
outVal.Add(item.Key, $"{newVal}");
}
// altrimenti siulazione random walk...
else
{
if (item.Value.factor == 1)
{
// uso factor come valore MAX ammesso
int randVal = rnd.Next(item.Value.minVal, item.Value.maxVal);
outVal.Add(item.Key, randVal.ToString());
}
else
{
// uso factor come fattore di divisione x simulare decimali
float randVal = ((float)rnd.Next(item.Value.minVal, item.Value.maxVal)) / (float)item.Value.factor;
outVal.Add(item.Key, randVal.ToString());
}
}
}
}
}
catch
{ }
lastSimData = DateTime.Now;
}
return outVal;
}
/// <summary>
/// Recupero programma in lavorazione
/// </summary>
/// <returns></returns>
public override string getPrgName()
{
// NOME DEL SIM!
string prgName = $"PROG_{cIobConf.codIOB}";
return prgName;
}
/// <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>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
}
/// <summary>
/// Processo contatori eventi...
/// </summary>
public override void processVHF()
{
if (lastEvCheck.AddMilliseconds(periodoMSec) < DateTime.Now)
{
// decremento contatore ultimo evento
try
{
if (bit2 != null)
bit2.wait--;
if (bit3 != null)
bit3.wait--;
if (bit4 != null)
bit4.wait--;
if (bit5 != null)
bit5.wait--;
if (simDich != null)
simDich.wait--;
if (simRC != null)
simRC.wait--;
if (simRS != null)
simRS.wait--;
}
catch
{ }
lastEvCheck = DateTime.Now;
}
}
/// <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);
// controlo SE sia disabilitata simulazione principale
if (!disableSimStatus)
{
// decodifica e gestione
decodeToBaseBitmap();
decodeOtherData();
reportRawInput(ref currDispData);
}
}
/// <summary>
/// Effettua reset del contapezzi
/// </summary>
/// <returns></returns>
public override bool resetcontapezziPLC()
{
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;
}
public override void tryConnect()
{
base.tryConnect();
connectionOk = true;
}
public override void tryDisconnect()
{
base.tryDisconnect();
connectionOk = false;
}
#endregion Public Methods
#region Protected Fields
/// <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>
/// Parametri simulazione oscillazione bit 6
/// </summary>
protected simPar bit6;
/// <summary>
/// Parametri simulazione oscillazione bit 7
/// </summary>
protected simPar bit7;
/// <summary>
/// pallet corrente
/// </summary>
protected int cP = 1;
protected bool disableSimStatus = false;
/// <summary>
/// ultimo controllo decremento eventi
/// </summary>
protected DateTime lastEvCheck;
/// <summary>
/// Ultimo istante in cui sono stati ridotti dati simulazione duration
/// </summary>
protected DateTime lastRedDuration;
/// <summary>
/// Ultimo istante in cui sono stati generati dati di simulazione
/// </summary>
protected DateTime lastSimData;
/// <summary>
/// Matricola OPR simulato
/// </summary>
protected int matrOpr = 1;
/// <summary>
/// Durata minima ODL x reset quando pezzi iob &gt; pezzi macchina...
/// </summary>
protected int minDurataODL = 480;
/// <summary>
/// pallet successivo (next)
/// </summary>
protected int nP = 1;
/// <summary>
/// periodo base del simulatore (in millisecondi)
/// </summary>
protected int periodoMSec = 1000;
/// <summary>
/// variabile di appoggio x stato segnale contapezzo
/// </summary>
protected bool sigPzCount = false;
/// <summary>
/// Simulazione registrazione dichiarazioni utente
/// </summary>
protected simPar simDich;
/// <summary>
/// BOOL: indica se simulare powerOn/Off (bit 0 e 1) compresi WarmUp e CoolDown
/// </summary>
protected bool simPowerOnOff;
/// <summary>
/// Simulazione effettuazione controlli utente
/// </summary>
protected simPar simRC;
/// <summary>
/// Simulazione registro scarti utente
/// </summary>
protected simPar simRS;
/// <summary>
/// Tempo di MINIMO attesa x simulazione parametri
/// </summary>
protected int waitSimPar = utils.CRI("waitSimPar");
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Salvo valori PLC
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
dataConf currMem = null;
int byteSize = 0;
byte[] MemBlock = new byte[1];
string memAddrWrite = "";
string serObj = "";
if (updatedPar != null)
{
// controllo i parametri... ne gestisco 4...
foreach (var item in updatedPar)
{
try
{
memAddrWrite = "";
// cerco in area memMapWrite...
if (memMap.mMapWrite.ContainsKey(item.uid))
{
// recupero!
currMem = memMap.mMapWrite[item.uid];
byteSize = currMem.size;
memAddrWrite = currMem.memAddr;
MemBlock = new byte[byteSize];
// faccio preliminarmente upsertKey...
upsertKey(currMem.name, currMem.value);
serObj = JsonConvert.SerializeObject(item, Formatting.Indented);
lgInfo($"Inizio processing plcWriteParams per {currMem.name} | valore richiesto {currMem.value}{Environment.NewLine}---------------UPDATED PARAM---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------");
serObj = JsonConvert.SerializeObject(currMem, Formatting.Indented);
lgInfo($"---------------MEMORY CONTENT---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------");
lgInfo($"---------------MemBlock data---------------{Environment.NewLine}{BitConverter.ToString(MemBlock)}{Environment.NewLine}--------------- END data ---------------");
}
else
{
lgInfo($"Errore uid non trovato in area write memory: {item.uid}, ci sono {memMap.mMapWrite.Count} in area write");
}
}
catch (Exception exc)
{
lgError($"Eccezione in fase di plcWriteParams per item {item.uid} con valore {item.value}{Environment.NewLine}{exc}");
}
}
}
}
/// <summary>
/// Decremento duration del bit indicato secondo stesse regole di ritardo dei bit di wait
/// </summary>
/// <param name="bit2proc"></param>
protected void tryRedDuration(int bit2proc)
{
if (lastRedDuration.AddMilliseconds(periodoMSec) < DateTime.Now)
{
switch (bit2proc)
{
case 2:
bit2.duration--;
break;
case 3:
bit3.duration--;
break;
case 4:
bit4.duration--;
break;
case 5:
bit5.duration--;
break;
case 6:
bit6.duration--;
break;
case 7:
bit7.duration--;
break;
default:
break;
}
lastRedDuration = DateTime.Now;
}
}
#endregion Protected Methods
#region Private Fields
private List<string> alarmMessages = new List<string>();
private int currSimAlarmCode = 0;
private Random rnd = new Random();
#endregion Private Fields
#region Private Methods
/// <summary>
/// Decodifica il resto dell'area x i dati accessori (allarmi, ...)
/// </summary>
private void decodeOtherData()
{
}
/// <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: allarme TCiclo (SLOW)
* B6: WarmUp_CoolDown
* B7: EmergArmed (1 = NON emergenza, 0 = emergenza)
* B8: pallet 1 (SE doppio pallet)
* B9: pallet 2 (SE doppio pallet)
----------------------------------------------------- */
// di base macchina in RUN
B_input = 3;
/*----------------------------------------
* Simulazione segnali con priorità:
* - Power ON / OFF (bit0/1)
* - ALLARMI
* - MANUALE
* - SLOW
* - contapezzi
* - emergenza
*
*----------------------------------------*/
// 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;
}
// aggiungo NON emergenza...
B_input += (1 << 7);
}
}
// 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 (contapezziPLC > contapezziIOB)
{
// invio SOLO SE sono OLTRE i numSim pz e li invio TUTTI in blocco
if ((contapezziPLC - contapezziIOB) > 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;
// salvo nuovo contapezzi (incremento di 1...) + richiesta refresh conteggio
contapezziIOB++;
needRefreshPzCount = true;
// invio conferma contapezzi..
string retVal = utils.callUrl($"{urlSetPzCount}{contapezziIOB}");
// verifica salvataggio
if (retVal != contapezziIOB.ToString())
{
// errore salvataggio contapezzi
lgInfo($"Errore salvataggio Contapezzi SIMULA 01: contapezziIOB {contapezziIOB} | risposta: {retVal}");
// rileggo il counter pezzi da server
pzCntReload(true);
}
}
else
{
string retVal = utils.callUrl($"{urlSetPzCount}{contapezziIOB}");
if (retVal != contapezziIOB.ToString())
{
// errore salvataggio contapezzi
lgInfo($"Errore salvataggio Contapezzi SIMULA 02: contapezziIOB {contapezziIOB} | risposta: {retVal}");
// rileggo il counter pezzi da server
pzCntReload(true);
}
sigPzCount = false;
}
}
lgInfo($"S01: Valori contatori: contapezzi macchina contapezziPLC: {contapezziPLC} | contapezziIOB: {contapezziIOB} | contapezziPLC > contapezziIOB");
}
}
else
{
// imposto currODL a vuoto!
currODL = "";
if (periodicLog)
{
lgInfo($"SIMULA | Lettura ODL non effettuata: IobOnline: {IobOnline} | currODL impostato a vuoto");
}
}
}
// questa parte la processo SOLO SE sono in run --> B_input == 3
if (B_input == 3)
{
// di base NON emergenza
B_input += (1 << 7);
// ora controllo il resto...
if (bit3.wait <= 0)
{
// segnalo BIT
B_input += (1 << 3);
// se non ancora fatto, simulo insorgenza allarme
if (currSimAlarmCode == 0)
{
// simulo una bitmap di max 8 allarmi, 1..255
currSimAlarmCode = rnd.Next(1, 255);
// invio allarme, 0 --> sim
sendAlarmVariations("SIM_BANK", 0, 0, (uint)currSimAlarmCode, alarmMessages);
}
// decremento duration
tryRedDuration(3);
// controllo se sia scaduta la duration... in quel caso reset...
if (bit3.duration <= 0)
{
bit3 = setupSimPar("SIM_ALARM");
// invio fine allarme: sim --> 0
sendAlarmVariations("SIM_BANK", 0, (uint)currSimAlarmCode, 0, alarmMessages);
// simulo terminazione allarme
currSimAlarmCode = 0;
}
}
else if (bit6.wait <= 0)
{
// segnalo BIT
B_input += (1 << 6);
// decremento duration
tryRedDuration(6);
// controllo se sia scaduta la duration... in quel caso reset...
if (bit6.duration <= 0)
{
bit6 = setupSimPar("SIM_WUCD");
}
// in warm up: provo split ODL
trySplitOdl();
}
else if (bit7.wait <= 0)
{
// segnalo BIT NEGATO (emergenza)
B_input -= (1 << 7);
// decremento duration
tryRedDuration(7);
// controllo se sia scaduta la duration... in quel caso reset...
if (bit7.duration <= 0)
{
bit7 = setupSimPar("SIM_EMRG");
}
// in emergenza: provo split ODL
trySplitOdl();
}
else if (bit4.wait <= 0)
{
// segnalo BIT
B_input += (1 << 4);
// decremento duration
tryRedDuration(4);
// 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 (bit2.wait <= 0)
{
// salvo nuovo contapezziPLC
var rand = new Random();
// se online vero delta (0..3) altrimenti 1
int delta = IobOnline ? rand.Next(1, 4) : 1;
if (!isMulti)
{
// solo se MP online...
if (MPOnline)
{
contapezziPLC += delta;
lgInfo($"S01: Valori contatori: contapezzi macchina contapezziPLC: {contapezziPLC} | contapezziIOB: {contapezziIOB} | 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 (contapezziIOB < contapezziPLC)
{
// segnalo BIT (1 pz)
B_input += (1 << 2);
}
}
// decremento duration
tryRedDuration(2);
// controllo se sia scaduta la duration... in quel caso reset...
if (bit2.duration <= 0)
{
bit2 = setupSimPar("SIM_PZCNT");
// salvo nuovo contapezzi (incremento di 1...) + richiesta refresh conteggio
contapezziIOB++;
needRefreshPzCount = true;
lgInfo($"S01: Valori contatori: contapezzi macchina contapezziPLC: {contapezziPLC} | contapezziIOB: {contapezziIOB} - incremento contapezzi per bit2.duration <= 0");
// invio SOLO SE il contapezzi PLC è <= contapezzi IOB...
if (contapezziPLC <= contapezziIOB)
{
sendContapezzi = true;
}
}
if (sendContapezzi)
{
// controllo se ALMENO sia pingabile il server
if (checkServerAlive)
{
// invio a server contapezzi (aggiornato)
string retVal = utils.callUrl(urlSetPzCount + contapezziIOB.ToString());
// verifica se tutto OK
if (retVal != contapezziIOB.ToString())
{
// errore salvataggio contapezzi
lgInfo($"Errore salvataggio Contapezzi SIMULA 03: contapezziIOB {contapezziIOB} | risposta: {retVal}");
// se non sono multi...
if (!isMulti)
{
// rileggo il counter pezzi da server
pzCntReload(true);
}
}
// resetto timer...
lastPzCountSend = DateTime.Now;
}
}
// provo a fare split ODL SE NON E' multi....
trySplitOdl();
}
else
{
// decremento duration
tryRedDuration(2);
if (bit2.duration <= 0)
{
bit2 = setupSimPar("SIM_PZCNT");
}
}
}
else if (bit5.wait <= 0 || tcMan.alarmDelayTC)
{
// segnalo BIT
B_input += (1 << 5);
// decremento duration
tryRedDuration(5);
// controllo se sia scaduta la duration... in quel caso reset...
if (bit5.duration <= 0)
{
bit5 = setupSimPar("SIM_SLOW");
}
}
// 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;
contapezziPLC++;
}
// se cP > 0 --> segnalo bit tavola...
if (cP == 1)
{
B_input += (1 << 8);
}
else if (cP == 2)
{
B_input += (1 << 9);
}
}
// gestione simulazione utente
if (simRC.wait <= 0)
{
// preparo record controlli... guasto se mi esce un secondo divisibile x 25
DateTime adesso = DateTime.Now;
int esitoNum = adesso.Second % 25 == 0 ? 0 : 1;
string note = esitoNum == 1 ? "" : $"SIM Controllo fallito alle {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
string sVal = $"MatrOpr: {matrOpr} | Esito: {esitoNum} | note: {note}";
// accodo x invio
accodaUserLog(sVal, qEncodeULog("RC", note, matrOpr, "", esitoNum));
// decremento duration
simRC.duration--;
// controllo se sia scaduta la duration... in quel caso reset...
if (simRC.duration <= 0)
{
simRC = setupSimPar("SIM_RC");
}
}
if (simRS.wait <= 0)
{
// preparo record controlli... guasto se mi esce un secondo divisibile x 25
DateTime adesso = DateTime.Now;
int causaleInt = adesso.Second % 7 + 1;
int numSca = adesso.Second % 5 + 1;
string causale = $"{causaleInt:00}";
string note = $"SIM Scarto [{causale} x {numSca}] {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
string sVal = $"MatrOpr: {matrOpr} | Causale: {causale} | note: {note}";
// accodo x invio
accodaUserLog(sVal, qEncodeULog("RS", note, matrOpr, causale, numSca));
// decremento duration
simRS.duration--;
// controllo se sia scaduta la duration... in quel caso reset...
if (simRS.duration <= 0)
{
simRS = setupSimPar("SIM_RS");
}
}
if (simDich.wait <= 0)
{
// preparo record dichiarazione...
DateTime adesso = DateTime.Now;
bool recLogin = adesso.Second % 5 == 0 ? false : true;
string note = recLogin ? "SIM Login Utente STEAMWARE USER" : $"SIM Nota automatica alle {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
string codTag = recLogin ? "UserLogin" : "Nota";
string sVal = $"MatrOpr: {matrOpr} | codTag: {codTag} | nota: {note}";
// accodo x invio
accodaUserLog(sVal, qEncodeULog("DI", note, matrOpr, codTag, 0));
// decremento duration
simDich.duration--;
// controllo se sia scaduta la duration... in quel caso reset...
if (simDich.duration <= 0)
{
simDich = setupSimPar("SIM_DICH");
}
}
// init obj display
newDisplayData currDispData = new newDisplayData();
currDispData.counter = contapezziIOB;
currDispData.semOut = Semaforo.SV;
raiseRefresh(currDispData);
}
}
/// <summary>
/// Setup parametri 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 (!string.IsNullOrEmpty(fullVal) && fullVal.IndexOf("|") > 0)
{
string[] param = fullVal.Split('|');
int.TryParse(param[0], out answ.wait);
int.TryParse(param[1], out answ.duration);
// simulo valore wait: in media target, range +/- 80%
answ.wait = rnd.Next(answ.wait / 5, answ.wait * 9 / 5);
// simulo valore duration: in media target, range +/- 50%
answ.duration = rnd.Next(answ.duration / 2, answ.duration * 3 / 2);
// verifico siano > 0...
answ.wait = answ.wait > 1 ? answ.wait : 2;
answ.duration = answ.duration > 1 ? answ.duration : 2;
}
}
}
return answ;
}
/// <summary>
/// provo a chiamare split ODL
/// </summary>
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();
}
}
}
#endregion Private Methods
}
/// <summary>
/// Configuraizone eventi da simulare
/// </summary>
public class simPar
{
#region Public Fields
/// <summary>
/// Durata dell'evento
/// </summary>
public int duration = 1;
/// <summary>
/// DateTime ultimo evento
/// </summary>
public DateTime lastEv = DateTime.Now;
/// <summary>
/// Attesa per evento
/// </summary>
public int wait = 10;
#endregion Public Fields
}
}