commit parziale gestione ricette

This commit is contained in:
Samuele Locatelli
2023-04-05 19:52:19 +02:00
parent dbc4a03df2
commit 206020281d
7 changed files with 293 additions and 327 deletions
+1
View File
@@ -116,6 +116,7 @@ namespace IOB_UT_NEXT
public string Note { get; set; } = "";
public string CodCli { get; set; } = "";
public DateTime InsertDate { get; set; } = DateTime.Now;
public string Recipe { get; set; } = "";
public string CodFase
{
+2 -2
View File
@@ -18,9 +18,9 @@ namespace IOB_UT_NEXT
/// </summary>
public Dictionary<string, int> fileDecod { get; set; } = new Dictionary<string, int>();
/// <summary>
/// Lista dei percorsi filepath attivi x gestioni IN/OUT varie
/// Lista ulteriori configurazioni KeyValuePair
/// </summary>
public Dictionary<string, string> actPath { get; set; } = new Dictionary<string, string>();
public Dictionary<string, string> optKVP { get; set; } = new Dictionary<string, string>();
}
}
+1 -1
View File
@@ -52,6 +52,6 @@ CLI_INST=SteamWareSim
;STARTLIST=IMI_RIMOR_VER_01
;STARTLIST=IMI_RIMOR_VER_02
;STARTLIST=IMI_PAMA_392
STARTLIST=IMI_PAMA_392
STARTLIST=Tend_FIMAT_01
MAXCNC=10
+3 -4
View File
@@ -63,21 +63,20 @@ ENABLE_DYN_DATA=TRUE
FORCE_DYN_DATA=TRUE
; gestione delta minimo accettabile tra min/MAX
DELTA_VAL=0.1
; clock base (da 10ms)
timerIntMs=10
; conf parametri memoria READ/WRITE
PARAM_CONF=Tend_FIMAT_01.json
NO_PING=FALSE
; conf blocchi memoria x READ
MEM_BLOCK=Tend_FIMAT_01_MBlock.json
; conf aree allarme
ALARM_CONF=Tend_FIMAT_01_alarm.json
; rimozione check limiti min/max dynData
disDynDataRangeCheck=true
; limite split QTA PODL
maxPodlQty=530
[BRANCH]
NAME=master
+14 -6
View File
@@ -304,12 +304,20 @@
"VarInt2Translate": "40001,40007",
"VarBit2Translate": "40003"
},
"actPath": {
"recTempPath": "DATA/CONF/RECIPE/FIMAT",
"recSentPath": "DATA/SENT",
"recDestPath": "y:/",
"ordExecPath": "Z:/",
"ordArchPath": "DATA/RETURN"
"optKVP": {
"maxPodlQty": 530,
"uselocalrecipe": true,
"path-locBase": "C:/MesData/Local",
"path-remBase": "C:/MesData/Remote",
//"path-localBase": "E:/MesData/",
"path-00-Arch": "ArchivioRicette/FIMAT",
"path-01-Temp": "01-Temp/FIMAT",
"path-02-Sent": "02-Inviate/FIMAT",
"path-03-Recv": "03-Ricevute/FIMAT",
"path-04-remW": "C:/MesData/Remote/Queue2",
"path-05-remR": "C:/MesData/Remote/Dosed"
//"path-04-remW": "//192.168.10.48/Queue2",
//"path-05-remR": "//192.168.10.48/Dosed"
},
"itemTranslation": {
"40001_0": "Stato non aggiornato per mancanza comunicazione da computer",
+42 -5
View File
@@ -602,7 +602,7 @@ namespace IOB_WIN_NEXT
public int counterULog { get; set; }
/// <summary>
/// Verifica se sia in modalità DEMO --&gt; da tipo IOB SIMULA...
/// Verifica se sia in modalità DEMO --> da tipo IOB SIMULA...
/// </summary>
public bool DemoIn
{
@@ -707,7 +707,7 @@ namespace IOB_WIN_NEXT
/// <summary>
/// Valore massimo accettato x incremento pezzi letti dal PLC (valore moltiplicato per
/// 100... 200% --&gt; 200)
/// 100... 200% --> 200)
/// </summary>
public int maxPzDeltaPerc
{
@@ -1158,7 +1158,7 @@ namespace IOB_WIN_NEXT
}
/// <summary>
/// URL per chiusura PODL --&gt; ODL corrente (metodo GET)...
/// URL per chiusura PODL --> ODL corrente (metodo GET)...
/// </summary>
public string urlPODLClose
{
@@ -1864,6 +1864,14 @@ namespace IOB_WIN_NEXT
return taskDone;
}
/// <summary>
/// Init parametri speciali, tipicamente KVP opzionali da json
/// </summary>
protected virtual void initSpecialParams()
{
}
/// <summary>
/// Effettua verifica delle memorie WRITE, se ci siano variazioni da riportare verso il PLC
/// Da impiegare ad esempio in caso di chiamate complesse (ad 2 aree di memoria condivise
@@ -2133,6 +2141,8 @@ namespace IOB_WIN_NEXT
processAutoOdl();
// verifico se devo gestire auto generazione dossier quotidiana
processAutoDossier();
// effettua gestione import file se configurato...
processFileImport();
}
else if (ciclo == gatherCycle.VLF)
{
@@ -2298,6 +2308,21 @@ namespace IOB_WIN_NEXT
}
return answ;
}
/// <summary>
/// Cerca se esiste il parametro opzionale nei KVP (JSON) e lo restituisce
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string getOptJsonKVP(string key)
{
string answ = "";
// controllo SE HO il parametro
if (memMap.optKVP.ContainsKey(key))
{
answ = memMap.optKVP[key];
}
return answ;
}
/// <summary>
/// Restituisce info OVERRIDES
@@ -4358,7 +4383,7 @@ namespace IOB_WIN_NEXT
protected bool needRefresh = true;
/// <summary>
/// Variabile numero errori vari (in lettura) --&gt; se supera soglia maxErroriCheck --&gt; disconnette
/// Variabile numero errori vari (in lettura) --> se supera soglia maxErroriCheck --> disconnette
/// </summary>
protected int numErroriCheck = 0;
@@ -5165,6 +5190,18 @@ namespace IOB_WIN_NEXT
}
return answ;
}
/// <summary>
/// Prepara le ricette dato path temp scaricando elenco PODL
/// </summary>
/// <param name="tempPath">Percorso temp di appoggio x preparare ricette (compreso nome macchina)</param>
/// <param name="useLocal">Indica se usare le copie locali delle ricette oppure richiedere da remoto (REST get)</param>
/// <returns></returns>
protected virtual bool iobWriteLocalRecipeFiles(string tempPath, bool useLocal)
{
bool answ = false;
// implementazione demo x ModbusTcp - FIMAT
return answ;
}
/// <summary>
/// Effettua traduzione ITEM da LUT parametrica (key: tipo+id) del file di conf, se non
@@ -6298,7 +6335,7 @@ namespace IOB_WIN_NEXT
}
/// <summary>
/// Effettua chiamata MP-IO per tentare chiusura PODL --&gt; ODL specifico
/// Effettua chiamata MP-IO per tentare chiusura PODL --> ODL specifico
/// </summary>
/// <param name="IdxODL"></param>
/// <returns></returns>
+230 -309
View File
@@ -1,6 +1,9 @@
using System;
using MathNet.Numerics;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.IO;
using System.Linq;
using static IOB_UT_NEXT.CustomObj;
namespace IOB_WIN_NEXT
@@ -28,6 +31,7 @@ namespace IOB_WIN_NEXT
{
lgInfo("NEW IOB ModBus TCP FIMAT");
initSpecialParams();
// provo lettura una prima volta i dati DYN
if (currPLC != null && currPLC.Connected)
{
@@ -47,310 +51,16 @@ namespace IOB_WIN_NEXT
}
}
/// <summary>
/// Elenco dei path validi x processing, tra cui
/// - archivio template ricette
/// - archivio PODL ricette inviate
/// - path remota invio PODL
/// - path remota lettura tasl ([P]ODL) svolti
/// - archivio task ([P]ODL) recuperati
/// </summary>
protected List<string> pathlist { get; set; } = new List<string>();
/// <summary>
/// Prepara la ricetta indicata partendo dai dati della ricetta template + dati ODL di riferimento
/// </summary>
/// <param name="recTemplatePath">Path del template della ricetta</param>
/// <param name="podlReq">Record del PODL da inviare</param>
/// <returns>Path della ricetta da inviare</returns>
protected string prepareRecipe(string recTemplatePath, PODLModel podlReq)
{
string recFilePath = "";
// leggo il file template ricetta
return recFilePath;
}
/// <summary>
/// Effettua invio XML del PODL dato il suo path verso la macchina
/// </summary>
/// <param name="odlRecipePath"></param>
/// <returns></returns>
protected bool sendRecipe(string odlRecipePath)
{
bool fatto = false;
return fatto;
}
/// <summary>
/// Verifica l'elenco dei PODL (Attivi) per la macchina e verifica se vadano inviati (controllando archivio PODL inviati
/// </summary>
/// <param name="actPOdlList"></param>
/// <returns></returns>
protected bool trySendPOdl(List<PODLModel> actPOdlList)
{
bool answ = false;
foreach (var item in actPOdlList)
{
// verifico se sia nella folder di quelli inviati
// se non presente preparo, leggendo conf del NUMERO ricetta (e configurandola come 10'000+num???)
// infine invio
}
return answ;
}
#endregion Public Constructors
#region Public Classes
#region Protected Fields
[XmlRoot(ElementName = "A_Recipe")]
public class ARecipe
{
#region Public Properties
/// <summary>
/// Quantità massima PODL prima di fare split ordine (default 999'999)
/// </summary>
protected int maxPodlQty = 999999;
[XmlElement(ElementName = "ColRecipe")]
public List<ColRecipe> ColRecipe { get; set; }
[XmlElement(ElementName = "DesRecipe")]
public DesRecipe DesRecipe { get; set; }
[XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Xsi { get; set; }
[XmlAttribute(AttributeName = "noNamespaceSchemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string NoNamespaceSchemaLocation { get; set; }
#endregion Public Properties
}
[XmlRoot(ElementName = "ColData")]
public class ColData
{
#region Public Properties
[XmlElement(ElementName = "ColourCode")]
public string ColourCode { get; set; }
[XmlElement(ElementName = "CompNumber")]
public int CompNumber { get; set; }
[XmlElement(ElementName = "Cost")]
public double Cost { get; set; }
[XmlElement(ElementName = "Date")]
public DateTime Date { get; set; }
[XmlElement(ElementName = "Description")]
public string Description { get; set; }
[XmlElement(ElementName = "DosDuration")]
public int DosDuration { get; set; }
[XmlElement(ElementName = "DosError")]
public string DosError { get; set; }
[XmlElement(ElementName = "DosNote")]
public object DosNote { get; set; }
[XmlElement(ElementName = "Origin")]
public string Origin { get; set; }
[XmlElement(ElementName = "PartsPerc")]
public double PartsPerc { get; set; }
[XmlElement(ElementName = "PartsWeight")]
public double PartsWeight { get; set; }
[XmlElement(ElementName = "TypComp")]
public string TypComp { get; set; }
[XmlElement(ElementName = "Weight-gr")]
public double Weightgr { get; set; }
[XmlElement(ElementName = "Weight-gr-dosed")]
public double Weightgrdosed { get; set; }
[XmlElement(ElementName = "Weight-gr-prev")]
public double Weightgrprev { get; set; }
[XmlElement(ElementName = "Weight-gr-total")]
public double Weightgrtotal { get; set; }
#endregion Public Properties
}
[XmlRoot(ElementName = "ColRecipe")]
public class ColRecipe
{
#region Public Properties
[XmlElement(ElementName = "ColData")]
public ColData ColData { get; set; }
#endregion Public Properties
}
[XmlRoot(ElementName = "DesData")]
public class DesData
{
#region Public Properties
[XmlElement(ElementName = "Article")]
public string Article { get; set; }
[XmlElement(ElementName = "CustDrumCode")]
public object CustDrumCode { get; set; }
[XmlElement(ElementName = "Customer")]
public object Customer { get; set; }
[XmlElement(ElementName = "Date")]
public DateTime Date { get; set; }
[XmlElement(ElementName = "Design")]
public int Design { get; set; }
[XmlElement(ElementName = "DosDuration")]
public int DosDuration { get; set; }
[XmlElement(ElementName = "DosedLine")]
public int DosedLine { get; set; }
[XmlElement(ElementName = "DosType")]
public string DosType { get; set; }
[XmlElement(ElementName = "DrumCode")]
public double DrumCode { get; set; }
[XmlElement(ElementName = "DrumInitialWeightKG")]
public double DrumInitialWeightKG { get; set; }
[XmlElement(ElementName = "DrumTag")]
public int DrumTag { get; set; }
[XmlElement(ElementName = "DrumType")]
public int DrumType { get; set; }
[XmlElement(ElementName = "Info1")]
public object Info1 { get; set; }
[XmlElement(ElementName = "Info2")]
public object Info2 { get; set; }
[XmlElement(ElementName = "Info3")]
public object Info3 { get; set; }
[XmlElement(ElementName = "Info4")]
public object Info4 { get; set; }
[XmlElement(ElementName = "Info5")]
public object Info5 { get; set; }
[XmlElement(ElementName = "Info6")]
public object Info6 { get; set; }
[XmlElement(ElementName = "Info7")]
public object Info7 { get; set; }
[XmlElement(ElementName = "Info8")]
public object Info8 { get; set; }
[XmlElement(ElementName = "Info9")]
public object Info9 { get; set; }
[XmlElement(ElementName = "LotID")]
public object LotID { get; set; }
[XmlElement(ElementName = "Note1")]
public string Note1 { get; set; }
[XmlElement(ElementName = "Note2")]
public object Note2 { get; set; }
[XmlElement(ElementName = "OrderCode")]
public string OrderCode { get; set; }
[XmlElement(ElementName = "Prio")]
public string Prio { get; set; }
[XmlElement(ElementName = "Quantity-kg")]
public double Quantitykg { get; set; }
[XmlElement(ElementName = "Quantity-kg-dosed")]
public double Quantitykgdosed { get; set; }
[XmlElement(ElementName = "RecipeStatus")]
public string RecipeStatus { get; set; }
[XmlElement(ElementName = "RecipeType")]
public string RecipeType { get; set; }
[XmlElement(ElementName = "RecName")]
public int RecName { get; set; }
[XmlElement(ElementName = "ReqType")]
public string ReqType { get; set; }
[XmlElement(ElementName = "Screen")]
public object Screen { get; set; }
[XmlElement(ElementName = "Sequence")]
public int Sequence { get; set; }
[XmlElement(ElementName = "SequenceTot")]
public int SequenceTot { get; set; }
[XmlElement(ElementName = "Series")]
public int Series { get; set; }
[XmlElement(ElementName = "ServiceType")]
public string ServiceType { get; set; }
[XmlElement(ElementName = "SplitCurrent")]
public int SplitCurrent { get; set; }
[XmlElement(ElementName = "SplitTotal")]
public int SplitTotal { get; set; }
[XmlElement(ElementName = "Taglio-D")]
public double TaglioD { get; set; }
[XmlElement(ElementName = "Taglio-N")]
public double TaglioN { get; set; }
[XmlElement(ElementName = "UM")]
public int UM { get; set; }
[XmlElement(ElementName = "Variant")]
public object Variant { get; set; }
[XmlElement(ElementName = "ViscoName")]
public object ViscoName { get; set; }
[XmlElement(ElementName = "ViscoValue")]
public double ViscoValue { get; set; }
#endregion Public Properties
}
// using System.Xml.Serialization; XmlSerializer serializer = new
// XmlSerializer(typeof(ARecipe)); using (StringReader reader = new StringReader(xml)) { var
// test = (ARecipe)serializer.Deserialize(reader); }
[XmlRoot(ElementName = "DesRecipe")]
public class DesRecipe
{
#region Public Properties
[XmlElement(ElementName = "DesData")]
public DesData DesData { get; set; }
#endregion Public Properties
}
#endregion Public Classes
#endregion Protected Fields
#region Protected Properties
@@ -387,6 +97,17 @@ namespace IOB_WIN_NEXT
}
}
/// <summary>
/// Elenco dei path validi x processing, tra cui
/// - archivio template ricette
/// - archivio PODL ricette inviate
/// - path remota invio PODL
/// - path remota lettura tasl ([P]ODL) svolti
/// - archivio task ([P]ODL) recuperati
/// </summary>
protected Dictionary<string, string> pathList { get; set; } = new Dictionary<string, string>();
/// <summary>
/// Restituisce status di WORK (auto + lavora), hard coded
/// </summary>
@@ -466,17 +187,217 @@ namespace IOB_WIN_NEXT
B_input = byteSignals;
}
/// <summary>
/// Effettua sync dati
/// Init parametri speciali, tipicamente KVP opzionali da json
/// </summary>
protected override void initSpecialParams()
{
// per prima cosa inizializzo lista PODL inviati
POdlSentList = POdlSentFileArch;
// verifico se usare ricette locali/http...
var sLocRec = getOptJsonKVP("useLocalRecipe");
bool.TryParse(sLocRec, out useLocalRecipe);
// recupero quantitativo massimo KG x PODL
var sMaxQty = getOptJsonKVP("maxPodlQty");
double.TryParse(sMaxQty, out maxQtyPerFile);
// recupero le folder specifiche x IN/OUT ricette filtrando direttamente l'area di memoria...
var dirList = memMap.optKVP
.Where(x => x.Key.StartsWith("path"))
.ToList();
pathList = dirList.ToDictionary(x => x.Key, x => x.Value);
}
protected string filePodlState = "temp/currPOdlSent.json";
protected bool useLocalRecipe = true;
protected double maxQtyPerFile = 0;
/// <summary>
/// Elenco corrente (in memory) PODL inviati all'impianto
/// chiave: idxPODL
/// valore: record PODL
/// </summary>
protected Dictionary<int, PODLModel> POdlSentList { get; set; } = new Dictionary<int, PODLModel>();
/// <summary>
/// Gestione archivio serializzato PODL inviati (tramite file temp locale)
/// </summary>
protected Dictionary<int, PODLModel> POdlSentFileArch
{
get
{
Dictionary<int, PODLModel> answ = new Dictionary<int, PODLModel>();
if (File.Exists(filePodlState))
{
string rawData = File.ReadAllText(filePodlState);
try
{
answ = JsonConvert.DeserializeObject<Dictionary<int, PODLModel>>(rawData);
lgInfo($"Rilettura status POdlSentFileArch: trovati {answ.Count} record");
}
catch (Exception exc)
{
lgError($"Errore in deserializzazione {filePodlState}{Environment.NewLine}{exc}");
}
}
return answ;
}
set
{
string rawVal = JsonConvert.SerializeObject(value);
File.WriteAllText(filePodlState, rawVal);
lgDebug($"Scrittura status POdlSentFileArch: trovati {value.Count} record");
}
}
/// <summary>
/// Prepara la ricetta indicata partendo dai dati della ricetta template + dati ODL di riferimento
/// </summary>
/// <param name="recFilePath">Path del file template della ricetta</param>
/// <param name="podlReq">Record del PODL da inviare</param>
/// <returns>Path della ricetta da inviare</returns>
protected bool prepareRecipe(string tempPath, string recFilePath, PODLModel podlReq)
{
bool fatto = false;
// leggo il file template ricetta
return fatto;
}
/// <summary>
/// Prepara le ricette in folder locale
/// </summary>
/// <param name="tempPath"></param>
/// <param name="useLocal"></param>
/// <returns></returns>
protected override bool iobWriteLocalRecipeFiles(string tempPath, bool useLocal)
{
bool fatto = false;
List<PODLModel> reqPOdlList = new List<PODLModel>();
// per prima cosa recupero elenco PODL da gestire....
var rawListPODL = callUrl(urlGetCurrPODL, false);
if (!string.IsNullOrEmpty(rawListPODL))
{
try
{
reqPOdlList = JsonConvert.DeserializeObject<List<PODLModel>>(rawListPODL);
fatto = reqPOdlList.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco PODL ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco PODL ({urlGetCurrPODL}) ha restituito valore vuoto");
}
// proseguo se ne ho trovati...
if (reqPOdlList.Count > 0)
{
// in questo elenco devo considerare solo PODL ATTIVI...
List<PODLModel> actPOdlList = reqPOdlList.Where(x => x.Attivabile).ToList();
// da questo elenco,se è rimasto qualcosa, ciclo x ricette da inviare
foreach (var actPodlReq in actPOdlList)
{
// devo escludere i PODL già gestiti
if (!POdlSentList.ContainsKey(actPodlReq.IdxPromessa))
{
string recFilePath = "";
try
{
// calcolo path ricetta...
recFilePath = Path.Combine(pathList["path-locBase"], pathList["path-00-Arch"], actPodlReq.Recipe);
// preparo file ricetta x PODL
bool fileOk = prepareRecipe(tempPath, recFilePath, actPodlReq);
if (fileOk)
{
// aggiungo PODL ad elenco dei processati
POdlSentList.Add(actPodlReq.IdxPromessa, actPodlReq);
// indico fatto (almeno per 1 è ok...)
fatto = true;
}
}
catch (Exception exc)
{
lgError($"iobWriteLocalRecipeFiles | Errore durante ciclo verifica ricetta | idxPODL {actPodlReq.IdxPromessa} | recFilePath {recFilePath}{Environment.NewLine}{exc}");
}
}
}
// salvo su file elenco PODL gestiti/inviati
POdlSentFileArch = POdlSentList;
}
return fatto;
}
/// <summary>
/// Effettua sync dati x PODL attivi
/// </summary>
protected override void processDataSync()
{
// richiesta check autoODL
processAutoOdl();
// richiesta generazione quotidiana dossiers
processAutoDossier();
// effettua gestione import file...
processFileImport();
string tempPath = Path.Combine(pathList["path-locBase"], pathList["path-01-Temp"]);
// recupero elenco PODL da processare, confronta con elenco dei PODL già inviati, scrive copia locale ricette
bool create = iobWriteLocalRecipeFiles(tempPath, useLocalRecipe);
if (create)
{
// invio ricette a impianto
bool trasmitted = sendRecipe("");
}
}
/// <summary>
/// Effettua invio XML del PODL dato il suo path verso la macchina
/// </summary>
/// <param name="odlRecipePath"></param>
/// <returns></returns>
protected bool sendRecipe(string odlRecipePath)
{
bool fatto = false;
return fatto;
}
/// <summary>
/// Verifica l'elenco dei PODL (Attivi) per la macchina e verifica se vadano inviati
/// (controllando archivio PODL inviati)
/// </summary>
/// <param name="actPOdlList"></param>
/// <returns></returns>
protected bool trySendPOdl(List<PODLModel> actPOdlList)
{
bool answ = false;
foreach (var item in actPOdlList)
{
// verifico se sia nella folder di quelli inviati
// se non presente preparo, leggendo conf del NUMERO ricetta (e configurandola come 10'000+num???)
// infine invio
}
return answ;
}
/// <summary>
/// Cerca di recuperare i file generati dall'impianto in merito al processing degli ordini
/// </summary>
/// <returns></returns>
protected override bool processFileImport()
{
bool fatto = false;
// cerca e sposta i NUOVI file
// per ogni file ricevuto controlla se sia corrispondente ad un file inviato (cerca PODL)
// se trova PODL prova ad avviare/chiudere PODL relativo (eventualmente duplicandolo)
return fatto;
}
#endregion Protected Methods