8bb0f158b5
- proj di base con le 2 form da ereditare - progetto globale che contiene TUTTI gli adapter (pronto a venire spezzettato - gettate le basi x "portare fuori" i vari componenti oppure fare compilazione condizonale
1987 lines
83 KiB
C#
1987 lines
83 KiB
C#
using IOB_UT_NEXT;
|
|
using MapoSDK;
|
|
using Newtonsoft.Json;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using static IOB_UT_NEXT.CustomObj;
|
|
|
|
namespace IOB_WIN_NEXT.Iob
|
|
{
|
|
/// <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
|
|
}
|
|
|
|
public class Simula : Iob.GenericNext
|
|
{
|
|
#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 Simula(AdapterFormNext 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;
|
|
// abilito subito scrittura coda IN
|
|
queueInEnabCurr = true;
|
|
// 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);
|
|
}
|
|
|
|
string sFluxOnRead = getOptPar("sendFluxOnRead");
|
|
if (!string.IsNullOrEmpty("sendFluxOnRead"))
|
|
{
|
|
bool.TryParse(sFluxOnRead, out sendFluxOnRead);
|
|
}
|
|
|
|
// gestione override contatore articoli...
|
|
if (!string.IsNullOrEmpty(getOptPar("NUM_ART_CHR_TRIM")))
|
|
{
|
|
string NUM_ART_CHR_TRIM = getOptPar("NUM_ART_CHR_TRIM");
|
|
bool.TryParse(NUM_ART_CHR_TRIM, out numArtCharTrim);
|
|
}
|
|
|
|
// riesegue setup PLC
|
|
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();
|
|
|
|
if (!string.IsNullOrEmpty(getOptPar("FTP_AT_START")) || !string.IsNullOrEmpty(getOptPar("DATA_SYNC_AT_START")))
|
|
{
|
|
// simulazione processo FTP (Cimolai) - FIXME TODO DeleteME
|
|
processDataSync();
|
|
}
|
|
// test ricetta
|
|
if (hasRecipe)
|
|
{
|
|
Calendar cal = new CultureInfo("it-IT").Calendar;
|
|
int week = cal.GetWeekOfYear(adesso, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
|
|
string tempPath = Path.Combine(pathList["path-locBase"], pathList["path-01-Temp"]);
|
|
string archPath = Path.Combine(pathList["path-locBase"], pathList["path-02-Sent"], $"{adesso:yyyy}", $"{week:00}");
|
|
string remoPath = Path.Combine(pathList["path-locBase"], pathList["path-04-remReq"]);
|
|
baseUtils.checkDir(tempPath);
|
|
baseUtils.checkDir(archPath);
|
|
try
|
|
{
|
|
// recupero elenco PODL da processare, check PODL già inviati, save locale
|
|
bool create = RecipeReqWriteLocal(tempPath, useLocalRecipe);
|
|
// invio ricette a impianto
|
|
bool trasmitted = RecipeSend(tempPath, archPath, remoPath);
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Eccezione in TEST processDataSync{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
|
|
// test gestione fileImport...
|
|
if (!string.IsNullOrEmpty(fileImportFolder))
|
|
{
|
|
processFileImport();
|
|
}
|
|
if (hasRecipe)
|
|
{
|
|
try
|
|
{
|
|
// effettua process ritorno ricette
|
|
processRecipeFileRet();
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Eccezione in TEST processRecipeFileRet{Environment.NewLine}{exc}");
|
|
}
|
|
try
|
|
{
|
|
// eseguo verifica/lancio ricette
|
|
RecipeDoProcCons();
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Eccezione in TEST RecipeDoProcCons{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
// commentato post test x Soitaab
|
|
if (EnabelPodlManFull)
|
|
{
|
|
// test decodifica log (tipo soitaab)
|
|
try
|
|
{
|
|
// eseguo subito un ciclo acquisizione log + processing
|
|
bool acquireLog = getRemoteLog();
|
|
if (acquireLog)
|
|
{
|
|
bool sentSignLog = processSignLogTable(adesso);
|
|
bool sentFluxLog = processFluxLogTable(adesso);
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Eccezione in IobFileSoitaab{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
}
|
|
|
|
#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 = "";
|
|
string newVal = "";
|
|
// verifico non sia null
|
|
if (task2exe != null)
|
|
{
|
|
// cerco task specifici
|
|
foreach (var item in task2exe)
|
|
{
|
|
taskVal = "";
|
|
newVal = "";
|
|
// 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:
|
|
// da memMap...
|
|
if (memMap != null && memMap.mMapWrite != null)
|
|
{
|
|
if (memMap.mMapWrite.ContainsKey(item.Key))
|
|
{
|
|
dataConf currMem = memMap.mMapWrite[item.Key];
|
|
string addr = currMem.memAddr;
|
|
taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
|
|
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
|
|
memMap.mMapWrite[item.Key].value = item.Value;
|
|
}
|
|
else
|
|
{
|
|
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value} ({item.Value})";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
taskVal = $"NO MemMap found, SET task: {item.Key} --> {item.Value}";
|
|
}
|
|
// salvo in currProd..
|
|
upsertKey(item.Key, item.Value);
|
|
//taskVal = processMemWriteRequests();
|
|
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.setArtNum:
|
|
// in primis faccio una chiamata per tutta la tab SE fosse vuoto il
|
|
// dict di traduzione
|
|
if (DictNumArt == null || DictNumArt.Count == 0)
|
|
{
|
|
getNumArt("");
|
|
}
|
|
// chiamo server x avere decodifica valore INT
|
|
newVal = getNumArt(item.Value);
|
|
if (numArtCharTrim)
|
|
{
|
|
string rawData = item.Value;
|
|
newVal = baseUtils.GetNumbers(rawData);
|
|
int newValInt = 0;
|
|
int.TryParse(newVal, out newValInt);
|
|
}
|
|
// procedo come il resto cercando mappatura in memMap: recupero dati
|
|
// da memMap...
|
|
if (memMap != null && memMap.mMapWrite != null)
|
|
{
|
|
if (memMap.mMapWrite.ContainsKey(item.Key))
|
|
{
|
|
dataConf currMem = memMap.mMapWrite[item.Key];
|
|
string addr = currMem.memAddr;
|
|
taskVal = $"SET task: {item.Key} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
|
|
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
|
|
memMap.mMapWrite[item.Key].value = newVal;
|
|
}
|
|
else
|
|
{
|
|
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {newVal} ({item.Value})";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
taskVal = $"NO MemMap found, SET task: {item.Key} --> {newVal} ({item.Value})";
|
|
}
|
|
|
|
// salvo in currProd..
|
|
upsertKey(item.Key, newVal);
|
|
break;
|
|
|
|
case taskType.setCommNum:
|
|
// chiamo server x avere decodifica valore INT
|
|
newVal = getNumComm(item.Value);
|
|
// procedo come il resto cercando mappatura in memMap: recupero dati
|
|
// da memMap...
|
|
if (memMap != null && memMap.mMapWrite != null)
|
|
{
|
|
if (memMap.mMapWrite.ContainsKey(item.Key))
|
|
{
|
|
dataConf currMem = memMap.mMapWrite[item.Key];
|
|
string addr = currMem.memAddr;
|
|
taskVal = $"SET task: {item.Key} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
|
|
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
|
|
memMap.mMapWrite[item.Key].value = newVal;
|
|
}
|
|
else
|
|
{
|
|
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {newVal} ({item.Value})";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
taskVal = $"NO MemMap found, SET task: {item.Key} --> {newVal} ({item.Value})";
|
|
}
|
|
|
|
// salvo in currProd..
|
|
upsertKey(item.Key, newVal);
|
|
break;
|
|
|
|
case taskType.nihil:
|
|
case taskType.fixStopSetup:
|
|
case taskType.sendWatchDogMes2Plc:
|
|
case taskType.startSetup:
|
|
case taskType.stopSetup:
|
|
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
|
|
break;
|
|
|
|
case taskType.forceResetPzCount:
|
|
// forzo sul SIM il valore a ZERO x l'IOB...
|
|
contapezziPLC = 0;
|
|
taskVal = "RESET contapezziPLC: 0";
|
|
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)
|
|
{
|
|
// forzo update
|
|
contapezziPLC = newPzCount;
|
|
taskVal = $"Set new contapezziPLC: {contapezziPLC}";
|
|
}
|
|
break;
|
|
|
|
case taskType.syncDbData:
|
|
processDataSync();
|
|
lgInfo("Richiesta syncDbData");
|
|
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()
|
|
{
|
|
bool useLUT = true;
|
|
// 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)
|
|
{
|
|
if (useLUT)
|
|
{
|
|
saveValueString(ref outVal, item.Key, item.Value.value);
|
|
}
|
|
else
|
|
{
|
|
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
|
|
double lastVal = 0;
|
|
double.TryParse(item.Value.value, out lastVal);
|
|
if (lastVal == 0)
|
|
{
|
|
lastVal = item.Value.maxVal - (double)item.Value.factor;
|
|
}
|
|
// decremento casuale...
|
|
double newVal = lastVal - ((double)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}";
|
|
if (useLUT)
|
|
{
|
|
saveValue(ref outVal, item.Key, newVal);
|
|
}
|
|
else
|
|
{
|
|
outVal.Add(item.Key, $"{newVal}");
|
|
}
|
|
}
|
|
// altrimenti simulazione random walk...
|
|
else
|
|
{
|
|
double randVal = 0;
|
|
if (item.Value.factor == 1)
|
|
{
|
|
// uso factor come valore MAX ammesso
|
|
randVal = rnd.Next(item.Value.minVal, item.Value.maxVal);
|
|
}
|
|
else
|
|
{
|
|
// uso factor come fattore di divisione x simulare decimali
|
|
randVal = ((float)rnd.Next(item.Value.minVal, item.Value.maxVal)) / (float)item.Value.factor;
|
|
}
|
|
// verifico uso LUT
|
|
if (useLUT)
|
|
{
|
|
saveValue(ref outVal, item.Key, randVal);
|
|
}
|
|
else
|
|
{
|
|
outVal.Add(item.Key, $"{randVal}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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
|
|
/// </summary>
|
|
/// <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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Formatta URL x invio in DataBlock Json dei dati FLog / eventi OVERRIDE x invio come tav
|
|
/// 1/2 per USER LOG....
|
|
/// </summary>
|
|
/// <param name="tipoUrl"></param>
|
|
/// <returns></returns>
|
|
public override string urlDataBlock(urlType tipoUrl)
|
|
{
|
|
// recupero tipo base
|
|
string answ = base.urlDataBlock(tipoUrl);
|
|
// se fosse una macchina doppio pallet...
|
|
if (isMulti)
|
|
{
|
|
// SOLO x la parte USER LOG
|
|
switch (tipoUrl)
|
|
{
|
|
case urlType.ULog:
|
|
string[] elencoMulti = null;
|
|
// devo chiamare cambio ODL x OGNI tavola: mi servono i parametri opzionali...
|
|
string IOB_MULTI_CNAME = getOptPar("IOB_MULTI_CNAME");
|
|
elencoMulti = IOB_MULTI_CNAME.Split(',');
|
|
// aggiungo tav 1 / 2 a caso secondo secondi attuali, se pari/dispari
|
|
int idxTav = DateTime.Now.Second % 2;
|
|
answ += $"#{elencoMulti[idxTav]}";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
#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>
|
|
/// Cartella logfile x test import (tipo Soitaab)
|
|
/// </summary>
|
|
protected string logDirPath = "C:\\MesData\\TestSoitaab";
|
|
|
|
/// <summary>
|
|
/// Matricola OPR simulato
|
|
/// </summary>
|
|
protected int matrOpr = 1;
|
|
|
|
/// <summary>
|
|
/// Durata minima ODL x reset quando pezzi iob > 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;
|
|
|
|
protected bool sendFluxOnRead = false;
|
|
|
|
/// <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>
|
|
/// Recupera file log da analizzare
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected override bool getRemoteLog()
|
|
{
|
|
bool answ = false;
|
|
|
|
// leggo direttamente il file di test in area C:\MesData\TestSoitaab
|
|
if (Directory.Exists(logDirPath))
|
|
{
|
|
string filePath = Path.Combine(logDirPath, $"Report-{DateTime.Today.Day}.txt");
|
|
bool hasDaily = File.Exists(filePath);
|
|
// cerco file quotidiano...
|
|
if (!hasDaily)
|
|
{
|
|
var fileList = Directory.GetFiles(logDirPath, "Report-*.txt");
|
|
DateTime lastMod = DateTime.Today.AddYears(-1);
|
|
foreach (var cFile in fileList)
|
|
{
|
|
var tempFile = Path.Combine(logDirPath, cFile);
|
|
var lwTime = File.GetLastWriteTime(tempFile);
|
|
if (lwTime >= lastMod)
|
|
{
|
|
lastMod = lwTime;
|
|
filePath = tempFile;
|
|
}
|
|
}
|
|
}
|
|
// leggo file
|
|
string rawVal = File.ReadAllText(Path.Combine(logDirPath, filePath));
|
|
if (!string.IsNullOrEmpty(rawVal))
|
|
{
|
|
// salvo in redis
|
|
string redKey = redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:Act");
|
|
redisMan.setRSV(redKey, rawVal);
|
|
answ = true;
|
|
}
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recupera da server set di dati specifici x IOB : qui per testare
|
|
/// - versione std: compilazione files CSV (ex FTP Cimolai)
|
|
/// - versione Kepware: test recupero parametri ricetta
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected override bool iobGetDataFromServer()
|
|
{
|
|
bool answ = false;
|
|
if (!string.IsNullOrEmpty(getOptPar("SIM_KWP")))
|
|
{
|
|
answ = iobGetSendDossierKepware();
|
|
}
|
|
else
|
|
{
|
|
answ = iobGetDataFromServerToFtp();
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <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;
|
|
// salvo in memMap...
|
|
currMem.value = item.reqValue;
|
|
// registro x invio
|
|
item.value = item.reqValue;
|
|
item.reqValue = "";
|
|
|
|
MemBlock = new byte[byteSize];
|
|
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 ---------------");
|
|
|
|
// salvo
|
|
upsertKey(item.uid, item.value);
|
|
}
|
|
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>
|
|
/// Effettua sync dati
|
|
/// </summary>
|
|
protected override void processDataSync()
|
|
{
|
|
try
|
|
{
|
|
// fixme todo: test scrittura file csv...
|
|
var taskGet = iobGetDataFromServer();
|
|
var taskWrite = iobWriteLocalCSV();
|
|
var taskSend = iobSendFTP("");
|
|
// richiesta check autoODL
|
|
processAutoOdl();
|
|
// richiesta generazione quotidiana dossiers
|
|
processAutoDossier();
|
|
// effettua gestione import file...
|
|
processFileImport();
|
|
// effettua process ritorno ricette
|
|
processRecipeFileRet();
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Eccezione in processDataSync:{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 / 20) < 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 signal log x soitaab | FixMe ToDo: eliminare
|
|
/// </summary>
|
|
/// <param name="val2test"></param>
|
|
/// <returns></returns>
|
|
private static int decodeSoitaabLog(string val2test)
|
|
{
|
|
// di default NON EMERGENZA...
|
|
int valInt = 128;
|
|
switch (val2test)
|
|
{
|
|
case "START;1":
|
|
valInt = 1 + 2 + 128;
|
|
break;
|
|
|
|
case "END;1":
|
|
valInt = 1 + 128;
|
|
break;
|
|
|
|
case "START;0":
|
|
case "END;0":
|
|
case "HOLD;":
|
|
valInt = 1 + 16 + 128;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return valInt;
|
|
}
|
|
|
|
/// <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)
|
|
{
|
|
// SE IOB online...
|
|
if (IobOnline)
|
|
{
|
|
// se il contapezzi è OLTRE il valore inviato....
|
|
if (contapezziPLC > contapezziIOB)
|
|
{
|
|
string machName = "";
|
|
if (isMulti)
|
|
{
|
|
machName = $"{cIobConf.codIOB}|TAV_{nP}";
|
|
}
|
|
// invio SOLO SE sono OLTRE i numSim pz e li invio TUTTI in blocco
|
|
if ((contapezziPLC - contapezziIOB) > minSendPzCountBlock)
|
|
{
|
|
sigPzCount = true;
|
|
trySendPzCountBlock(machName);
|
|
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 fullUrl = $"{urlSetPzCount}{contapezziIOB}";
|
|
if (isMulti)
|
|
{
|
|
fullUrl = $"{urlSetPzCount.Replace(cIobConf.codIOB, $"{cIobConf.codIOB}|TAV_{nP}")}{contapezziPLC}";
|
|
}
|
|
string retVal = utils.callUrl(fullUrl);
|
|
// 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, machName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string retVal = utils.callUrl($"{urlSetPzCount}{contapezziIOB}");
|
|
if (retVal != contapezziIOB.ToString())
|
|
{
|
|
// errore salvataggio contapezzi
|
|
lgInfo($"Errore salvataggio Contapezzi SIMULA 02: contapezziIOB {contapezziIOB} | risposta: {retVal}");
|
|
if (!isMulti)
|
|
{
|
|
// rileggo il counter pezzi da server
|
|
pzCntReload(true, machName);
|
|
}
|
|
}
|
|
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 || B_input == 7)
|
|
{
|
|
// di base NON emergenza
|
|
B_input += (1 << 7);
|
|
// ora controllo il resto...
|
|
if (bit3 != null && 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;
|
|
}
|
|
|
|
tryConfirm();
|
|
}
|
|
else if (bit6 != null && 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
|
|
processAutoOdl();
|
|
}
|
|
else if (bit7 != null && 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
|
|
processAutoOdl();
|
|
}
|
|
else if (bit4 != null && 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
|
|
processAutoOdl();
|
|
}
|
|
else if (bit2 != null && bit2.wait <= 0)
|
|
{
|
|
// salvo nuovo contapezziPLC
|
|
var rand = new Random();
|
|
// se online vero delta (1..10) altrimenti 1
|
|
int delta = IobOnline ? rand.Next(1, 10) : 1;
|
|
if (MPOnline)
|
|
{
|
|
// solo se MP online...
|
|
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)
|
|
{
|
|
string fullUrl = $"{urlSetPzCount}{contapezziIOB}";
|
|
// invio a server contapezzi (aggiornato)
|
|
if (isMulti)
|
|
{
|
|
fullUrl = $"{urlSetPzCount.Replace(cIobConf.codIOB, $"{cIobConf.codIOB}|TAV_{nP}")}{contapezziIOB}";
|
|
}
|
|
string retVal = utils.callUrl(fullUrl);
|
|
// 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...
|
|
processAutoOdl();
|
|
}
|
|
else
|
|
{
|
|
// decremento duration
|
|
tryRedDuration(2);
|
|
if (bit2.duration <= 0)
|
|
{
|
|
bit2 = setupSimPar("SIM_PZCNT");
|
|
}
|
|
}
|
|
}
|
|
else if (bit5 != null && 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++;
|
|
// invio contapezzi...
|
|
string fullUrl = $"{urlSetPzCount.Replace(cIobConf.codIOB, $"{cIobConf.codIOB}|TAV_{nP}")}{contapezziIOB}";
|
|
string retVal = utils.callUrl(fullUrl);
|
|
}
|
|
// 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 != null && 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 != null && 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 != null && 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>
|
|
/// Recupero dati da server x costruzione files CSV da inviare via FTP (ex: Cimolai)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private bool iobGetDataFromServerToFtp()
|
|
{
|
|
bool answ;
|
|
bool okArt = false;
|
|
bool okDoss = false;
|
|
bool okPodl = false;
|
|
bool okLVFasi = false;
|
|
List<PODLModel> listaPODL = new List<PODLModel>();
|
|
List<DossiersModel> listaDoss = new List<DossiersModel>();
|
|
List<AnagArticoli> listaArt = new List<AnagArticoli>();
|
|
List<ListVal> anagLVFasi = new List<ListVal>();
|
|
Dictionary<string, string> dictAF = new Dictionary<string, string>();
|
|
// recupera dati da server tramite chiamate REST a MP/IO/IOB...
|
|
var rawListArt = callUrl(urlGetCurrArt, false);
|
|
var rawListDOSS = callUrl(urlGetCurrDOSS, false);
|
|
var rawListPODL = callUrl(urlGetCurrPODL, false);
|
|
var rawLVFasi = callUrl(urlGetListValFasiPodl, false);
|
|
if (!string.IsNullOrEmpty(rawListArt))
|
|
{
|
|
try
|
|
{
|
|
listaArt = JsonConvert.DeserializeObject<List<AnagArticoli>>(rawListArt);
|
|
okArt = listaArt.Count > 0;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error($"Errore: chiamata elenco ART ha restituito errore{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lg.Error($"Errore: chiamata elenco ART ({urlGetCurrArt}) ha restituito valore vuoto");
|
|
}
|
|
if (!string.IsNullOrEmpty(rawListDOSS))
|
|
{
|
|
try
|
|
{
|
|
listaDoss = JsonConvert.DeserializeObject<List<DossiersModel>>(rawListDOSS);
|
|
okDoss = listaDoss.Count > 0;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lg.Error($"Errore: chiamata elenco DOSSIER ({urlGetCurrDOSS}) ha restituito valore vuoto");
|
|
}
|
|
if (!string.IsNullOrEmpty(rawListPODL))
|
|
{
|
|
try
|
|
{
|
|
listaPODL = JsonConvert.DeserializeObject<List<PODLModel>>(rawListPODL);
|
|
okPodl = listaPODL.Count > 0;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lg.Error($"Errore: chiamata elenco PODL ({urlGetCurrPODL}) ha restituito valore vuoto");
|
|
}
|
|
if (!string.IsNullOrEmpty(rawLVFasi))
|
|
{
|
|
try
|
|
{
|
|
anagLVFasi = JsonConvert.DeserializeObject<List<ListVal>>(rawLVFasi);
|
|
dictAF = anagLVFasi.ToDictionary(x => x.value, x => x.label);
|
|
okLVFasi = listaArt.Count > 0;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error($"Errore: chiamata elenco ListVal ha restituito errore{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lg.Error($"Errore: chiamata elenco ListVal ({urlGetListValFasiPodl}) ha restituito valore vuoto");
|
|
}
|
|
answ = okPodl && okDoss && okArt && okLVFasi;
|
|
if (answ)
|
|
{
|
|
// predispongo dati PODL
|
|
ListaJobs = listaPODL
|
|
.Select(x => new JobRow() { Matricola = x.CodArticolo, Commessa = $"PODL{x.IdxPromessa:00000000}", Articolo = x.CodArticolo, Descrizione = x.CodArticolo, DataIns = $"{x.InsertDate:dd/MM/yyyy}", OraIns = $"{x.InsertDate:HH:mm}", Lavorazione = $"{getLV(dictAF, x.KeyRichiesta)} {x.Note}".Trim() })
|
|
.ToList();
|
|
|
|
// predispongo dati articoli
|
|
ListaArticoli = listaArt
|
|
.Select(a => new ArtRow() { Matricola = a.CodArticolo, Articolo = a.Disegno, Descrizione = a.DescArticolo, LimiteVel = 100 })
|
|
.Distinct()
|
|
.ToList();
|
|
|
|
// completo con dati DOSSIER
|
|
foreach (var item in ListaArticoli)
|
|
{
|
|
string codArt = item.Matricola;
|
|
var currDoss = listaDoss.Where(x => x.CodArticolo == codArt).FirstOrDefault();
|
|
if (currDoss != null)
|
|
{
|
|
DossierFluxLogDTO resultSet = JsonConvert.DeserializeObject<DossierFluxLogDTO>(currDoss.Valore);
|
|
// traduco AD MENTULAM...
|
|
item.Peso_01 = getFluxValInt(resultSet, "POS_X");
|
|
item.Peso_02 = getFluxValInt(resultSet, "POS_Y");
|
|
item.Peso_03 = getFluxValInt(resultSet, "RAPID_OVER");
|
|
item.Peso_04 = getFluxValInt(resultSet, "POS_Z");
|
|
item.PosizCarrello_01 = getFluxValInt(resultSet, "TEMP_01");
|
|
item.PosizCarrello_02 = getFluxValInt(resultSet, "RAPID_OVER");
|
|
item.PosizCarrello_03 = getFluxValInt(resultSet, "POWER_01");
|
|
item.PosizCarrello_04 = getFluxValInt(resultSet, "POS_X");
|
|
}
|
|
}
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recupero dati da server x invio dati ricetta a KepWare (Ex RAMA x TFT)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private bool iobGetSendDossierKepware()
|
|
{
|
|
bool answ;
|
|
|
|
lg.Info("iobGetSendDossier: Inizio scrittura dati a KepWare");
|
|
/*-----------------------------------------
|
|
* STEPS processo:
|
|
* - recupero ARTICOLI (solo quelli dei PODL attivi)
|
|
* - recupero PODL (tutti)
|
|
* - recupero elenco DOSSIER last x macchina (legato ai PODL attivi)
|
|
* - prendo SOLO PODL attivi, e prendo il + vecchio
|
|
* - se trovo dossier x articolo del PODL + vecchio (e dovrebbe...) procedo
|
|
* - uso Dictionary RecipeKeyTranslate x tradurre l'elenco fluxLog in nuovi valori
|
|
* - preparo il set di valori da scrivere con traduzione
|
|
* - invio scritture (sim test --> scrivo su file?)
|
|
* - aggiungo info articolo (+ ODL...)
|
|
* - aggiungo il valore di "scrittura effettuata"
|
|
*----------------------------------------- */
|
|
|
|
bool okArt = false;
|
|
bool okDoss = false;
|
|
bool okPodl = false;
|
|
List<AnagArticoli> listaArt = new List<AnagArticoli>();
|
|
List<PODLModel> listaPODL = new List<PODLModel>();
|
|
List<DossiersModel> listaDoss = new List<DossiersModel>();
|
|
// recupera dati da server tramite chiamate REST a MP/IO/IOB...
|
|
var rawListArt = callUrl(urlGetCurrArt, false);
|
|
var rawListDOSS = callUrl(urlGetCurrDOSS, false);
|
|
var rawListPODL = callUrl(urlGetCurrPODL, false);
|
|
if (!string.IsNullOrEmpty(rawListArt))
|
|
{
|
|
try
|
|
{
|
|
listaArt = JsonConvert.DeserializeObject<List<AnagArticoli>>(rawListArt);
|
|
okArt = listaArt.Count > 0;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error($"Errore: chiamata elenco ART ha restituito errore{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lg.Error($"Errore: chiamata elenco ART ({urlGetCurrArt}) ha restituito valore vuoto");
|
|
}
|
|
if (!string.IsNullOrEmpty(rawListDOSS))
|
|
{
|
|
try
|
|
{
|
|
listaDoss = JsonConvert.DeserializeObject<List<DossiersModel>>(rawListDOSS);
|
|
okDoss = listaDoss.Count > 0;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lg.Error($"Errore: chiamata elenco DOSSIER ({urlGetCurrDOSS}) ha restituito valore vuoto");
|
|
}
|
|
if (!string.IsNullOrEmpty(rawListPODL))
|
|
{
|
|
try
|
|
{
|
|
listaPODL = JsonConvert.DeserializeObject<List<PODLModel>>(rawListPODL);
|
|
okPodl = listaPODL.Count > 0;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lg.Error($"Errore: chiamata elenco PODL ({urlGetCurrPODL}) ha restituito valore vuoto");
|
|
}
|
|
|
|
answ = okArt && okPodl && okDoss;
|
|
if (answ)
|
|
{
|
|
// verifico i PODL attivi...
|
|
var listPodlAct = listaPODL.Where(x => x.Attivabile).OrderBy(x => x.InsertDate).ToList();
|
|
if (listPodlAct.Count > 0)
|
|
{
|
|
string codArt = listPodlAct.FirstOrDefault().CodArticolo;
|
|
// verifico di avere il dossier e l'articolo x questo PODL
|
|
var selArt = listaArt.Where(x => x.CodArticolo == codArt).FirstOrDefault();
|
|
var selDoss = listaDoss.Where(x => x.CodArticolo == codArt).FirstOrDefault();
|
|
// prendo il primo e recupero...
|
|
if (selArt != null && selDoss != null)
|
|
{
|
|
// recupero il resultset dei valori FluxLog da caricare
|
|
DossierFluxLogDTO resultSet = JsonConvert.DeserializeObject<DossierFluxLogDTO>(selDoss.Valore);
|
|
List<FluxLog> listFluxLog = resultSet.ODL;
|
|
|
|
// preparo elenco parametri da inviare...
|
|
ListaCalcParams = new List<objItem>();
|
|
// per ogni valore ricevuto nel dossier --> calcolo parametro da inviare
|
|
foreach (var item in listFluxLog)
|
|
{
|
|
objItem currData = new objItem()
|
|
{
|
|
// valori dentro OPC-UA
|
|
//uid = $"{opcUaParams.BaseKeyTranslate}{item.CodFlux}",
|
|
uid = item.CodFlux,
|
|
reqValue = item.Valore,
|
|
name = item.CodFlux
|
|
};
|
|
|
|
// aggiungo!
|
|
ListaCalcParams.Add(currData);
|
|
}
|
|
// effettua chiamata scrittura verso impianto...
|
|
plcWriteParams(ref ListaCalcParams);
|
|
}
|
|
}
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Esegue processing + invio dati tab SignLog | FixMe ToDo: eliminare
|
|
/// </summary>
|
|
/// <param name="adesso"></param>
|
|
/// <returns></returns>
|
|
private bool processFluxLogTable(DateTime adesso)
|
|
{
|
|
bool fatto = false;
|
|
// leggo eventuali dati di fluxlog ed invio...
|
|
if (fluxLogData != null && fluxLogData.Count > 0)
|
|
{
|
|
string sVal = "";
|
|
foreach (var fLog2send in fluxLogData)
|
|
{
|
|
var kvpFlux = fLog2send.valString.Split(';');
|
|
if (kvpFlux.Count() > 1)
|
|
{
|
|
sVal = $"[DYNDATA] |{fLog2send.dtRif:yyyy-MM-dd HH:mm:ss}|{kvpFlux[0]}|{kvpFlux[1]}";
|
|
// chiamo accodamento con dataora corretta...
|
|
accodaFLog(sVal, qEncodeFLog(fLog2send.dtRif, kvpFlux[0], kvpFlux[1]));
|
|
}
|
|
}
|
|
// svuoto coda invio fluxlog...
|
|
fluxLogData = new List<GenLogRow>();
|
|
// fatto!
|
|
fatto = true;
|
|
}
|
|
return fatto;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Esegue processing + invio dati tab SignLog
|
|
/// </summary>
|
|
/// <param name="adesso"></param>
|
|
/// <returns></returns>
|
|
private bool processSignLogTable(DateTime adesso)
|
|
{
|
|
bool fatto = false;
|
|
// init oggetto x processing
|
|
Dictionary<DateTime, int> sigLogFromFile = new Dictionary<DateTime, int>();
|
|
// recupero i dati dai logs files, attuale e nuovo...
|
|
string fileAct = redisMan.getRSV(redKeyLogfileAct);
|
|
string fileLast = redisMan.getRSV(redKeyLogfileLast);
|
|
// confronto con i dati dell'ultimo LOG FILE processato
|
|
int lenghtAct = fileAct != null ? fileAct.Length : 0;
|
|
int lenghtLast = fileLast != null ? fileLast.Length : 0;
|
|
//if (lenghtAct > 0 && lenghtAct != lenghtLast)
|
|
if (lenghtAct > 0)
|
|
{
|
|
List<GenLogRow> logNew = new List<GenLogRow>();
|
|
List<GenLogRow> sigLogData = new List<GenLogRow>();
|
|
List<GenLogRow> logLast = getGenLogFromMachineLog(fileLast);
|
|
List<GenLogRow> logAct = getGenLogFromMachineLog(fileAct);
|
|
// SE c'è prendo ultima riga inviata x confronto...
|
|
var fileLastEnd = logLast.LastOrDefault();
|
|
if (fileLastEnd != null)
|
|
{
|
|
// ora prendo SOLAMENTE le righe nuove
|
|
logNew = logAct
|
|
.Where(x => x.dtRif > fileLastEnd.dtRif)
|
|
.ToList();
|
|
}
|
|
else
|
|
{
|
|
logNew = logAct;
|
|
}
|
|
// solo SE ho nuovi dati....
|
|
if (logNew.Count > 0)
|
|
{
|
|
// processo le righe nuove e le accodo in redis come elenco FluxLog da inviare
|
|
// (appoggio in redis)... andando ad accodarle...
|
|
var currFLD = fluxLogData;
|
|
currFLD.AddRange(logNew);
|
|
fluxLogData = currFLD;
|
|
|
|
// estraggo solo info x sig log
|
|
sigLogData = logNew
|
|
.Where(x => x.valString.StartsWith("START") || x.valString.StartsWith("HOLD") || x.valString.StartsWith("END"))
|
|
.OrderBy(x => x.dtRif)
|
|
.ToList();
|
|
|
|
// processo le righe nuove trasformando al volo SOLO eventi...
|
|
foreach (var sLog2send in sigLogData)
|
|
{
|
|
/* -----------------------------------------------------
|
|
* bitmap MAPO STANDARD 60
|
|
* B0: 001 POWER_ON
|
|
* B1: 002 RUN
|
|
* B2: 004 pzCount
|
|
* B3: 008 allarme
|
|
* B4: 016 manuale
|
|
* B5: 032 slowTC
|
|
* B6: 064 WarmUpCoolDown
|
|
* B7: 128 EmergArmata
|
|
*
|
|
----------------------------------------------------- */
|
|
|
|
int valInt = decodeSoitaabLog(sLog2send.valString);
|
|
|
|
string currVal = getEncodSigLog(sLog2send.dtRif, valInt, counterSigIN);
|
|
// verifico non sia in veto invio iniziale...
|
|
if (queueInEnabCurr)
|
|
{
|
|
// --> accodo (valore già formattato)!
|
|
QueueIN.Enqueue(currVal);
|
|
// loggo!
|
|
lgTrace(string.Format("[QUEUE-IN] {0}", currVal));
|
|
counterSigIN++;
|
|
if (counterSigIN > 9999)
|
|
{
|
|
counterSigIN = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lgDebug($"[VETO FOR QUEUE-IN] | {currVal} - MESSAGE NOT SENT | {adesso:yyyyMMdd_HHmmss}");
|
|
checkVetoQueueIn();
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------
|
|
* processo righe x costruire una lista di eventi produzione:
|
|
* - elenco di eventi contapezzi
|
|
* - lista xODL come articolo + quantità prodotta (_1, _2, ...)
|
|
* - eventuale ultimo xODL che resta aperto
|
|
*
|
|
* successivamente processing + invio info raccolte
|
|
* - recupero PODL aperti
|
|
* - apertura/chiusura PODL esistenti
|
|
* - creazione PODL mancanti
|
|
* - dichiarazione contapezzi
|
|
*
|
|
* ---------------------------------------------------------- */
|
|
|
|
string sVal = "";
|
|
string descrArt = "";
|
|
List<ProdBatchData> elencoProdBatch = new List<ProdBatchData>();
|
|
ProdBatchData lastProdBatch = new ProdBatchData();
|
|
|
|
// ora processo TUTTO x il generico caso fluxLog......
|
|
foreach (var fLog2send in logNew)
|
|
{
|
|
// separo per ";"
|
|
var currData = fLog2send.valString.Split(';');
|
|
if (currData.Length > 1)
|
|
{
|
|
if (sendFluxOnRead)
|
|
{
|
|
// per prima cosa fluxLog a prescindere...
|
|
sVal = $"[LOGFILE]{currData[0]}|{currData[1]}";
|
|
// ...e chiamo accodamento
|
|
accodaFLog(sVal, qEncodeFLog(fLog2send.dtRif, currData[0], currData[1]));
|
|
}
|
|
|
|
switch (currData[0])
|
|
{
|
|
case "END":
|
|
// se è un end --> è comunque NON + running
|
|
isRunningState = false; // chiudo batch precedente + in elenco (SE era valido...)
|
|
if (!string.IsNullOrEmpty(lastProdBatch.codArt))
|
|
{
|
|
lastProdBatch.dtEnd = fLog2send.dtRif;
|
|
elencoProdBatch.Add(lastProdBatch);
|
|
}
|
|
// reset articolo..
|
|
lastArtDescr = "";
|
|
// nuovo batch VUOTO
|
|
lastProdBatch = new ProdBatchData();
|
|
contapezziPLC = 0;
|
|
break;
|
|
|
|
case "START":
|
|
isRunningState = currData[1] == "1";
|
|
break;
|
|
|
|
case "PARTCODE":
|
|
// inizio confronto articolo con precedente x capire se sia NUOVO
|
|
descrArt = currData[1];
|
|
// devo scartare i "doppi percorsi"
|
|
if (descrArt != lastArtDescr)
|
|
{
|
|
// verifico se sia DAVVERO cambiato articolo... prendo
|
|
// articolo senza indice (_1, _2, _3, ...)
|
|
var artDataLast = lastArtDescr.Split('_');
|
|
var artDataNew = descrArt.Split('_');
|
|
// nuovo articolo (NON vuoto)
|
|
if (!string.IsNullOrEmpty(artDataNew[0]) && artDataLast[0] != artDataNew[0])
|
|
{
|
|
// chiudo batch precedente + in elenco (SE era valido...)
|
|
if (!string.IsNullOrEmpty(lastProdBatch.codArt))
|
|
{
|
|
// chiudo prod
|
|
lastProdBatch.dtEnd = fLog2send.dtRif;
|
|
elencoProdBatch.Add(lastProdBatch);
|
|
}
|
|
// nuovo batch
|
|
lastProdBatch = new ProdBatchData()
|
|
{
|
|
codArt = artDataNew[0],
|
|
dtStart = fLog2send.dtRif,
|
|
numPz = 1
|
|
};
|
|
}
|
|
// solo nuova copia articolo
|
|
else
|
|
{
|
|
// aumento numPezzi batch
|
|
lastProdBatch.numPz++;
|
|
}
|
|
|
|
// salvo nuovo art processato
|
|
lastArtDescr = descrArt;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// recupero elenco PODL corrente...
|
|
List<PODLModel> reqPOdlList = MachineNextPodl();
|
|
// adesso processo tutti i prodBatch collezionati x invio
|
|
foreach (var item in elencoProdBatch)
|
|
{
|
|
int currIdxPOdl = 0;
|
|
// cerco se articolo sia in elenco PODL
|
|
var listPOdl = reqPOdlList
|
|
.Where(x => x.CodArticolo.Equals(item.codArt, StringComparison.InvariantCultureIgnoreCase))
|
|
.ToList();
|
|
|
|
// se trovato --> invio start/stop PODL
|
|
if (listPOdl != null && listPOdl.Count > 0)
|
|
{
|
|
var currPodl = listPOdl.OrderBy(x => x.IdxPromessa).FirstOrDefault();
|
|
if (currPodl != null)
|
|
{
|
|
currIdxPOdl = currPodl.IdxPromessa;
|
|
// elimino da elenco PODL
|
|
reqPOdlList.Remove(currPodl);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// se NON trovato --> creo PODL, poi avvio/chiudo
|
|
currIdxPOdl = TryCreatePodl(item.codArt, CodGruppoIob, item.numPz);
|
|
}
|
|
// se idxPOdl valido
|
|
if (currIdxPOdl > 0)
|
|
{
|
|
// mando apertura PODL
|
|
SendStartPodl(currIdxPOdl, item.dtStart);
|
|
// chiudo PODL...
|
|
if (item.dtEnd != null)
|
|
{
|
|
// invio pezzi 3 sec prima di chiusura
|
|
SendPzIncrAtDate(item.numPz, ((DateTime)item.dtEnd).AddSeconds(-3));
|
|
// chiudo
|
|
SendClosePOdl(currIdxPOdl, (DateTime)item.dtEnd);
|
|
}
|
|
}
|
|
}
|
|
|
|
// salvo in last il valore di act...
|
|
redisMan.setRSV(redKeyLogfileLast, fileAct);
|
|
}
|
|
fatto = true;
|
|
}
|
|
return fatto;
|
|
}
|
|
|
|
/// <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 ad effettuare conferma pezzi SE sono oltre uno scalino del 10% dall'ultima conferma...
|
|
/// </summary>
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
private void tryConfirm()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// provo a chiamare split ODL
|
|
/// </summary>
|
|
private void trySplitOdl()
|
|
{
|
|
// 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
|
|
}
|
|
} |