using Newtonsoft.Json; using Newtonsoft.Json.Converters; using SteamWare; using System; using System.Collections.Generic; namespace AppData { /// /// Classe con metodi di supporto per COMUNICAZIONE /// public class ComLib { /// /// Wrapper traduzione termini /// /// /// public static string traduci(string lemma) { return user_std.UtSn.Traduci(lemma); } #region conf posizioni redis protected static string redOutPath = "NKC:SERV:BREQ"; protected static string redMsgCount = "NKC:SERV:BREQ:MCount"; protected static string redMsgList = "NKC:SERV:BREQ:MList"; protected static string redMLCurrStack = "NKC:SERV:TAKT:CurrStack"; protected static string redNestAnsw = "NKC:NEST:BANSW"; protected static string redProdReq = "NKC:SERV:BUNKS"; protected static string redProdAnsw = "NKC:PROD:BUNKS"; #endregion #region definizione classi impiegate con PROD /// /// Tipologia di macchina /// public enum mType { Multiax = 0, Offline } /// /// TIpologia di ordine /// public enum oType { BatchRequest = 0, OfflineOrder } /// /// Stati degli oggetti TAKT e Stack /// public enum CStatus { /// /// Programmato /// Programmed = 0, /// /// In corso /// Running, /// /// Completato /// Done } /// /// Stati degli oggetti Batch /// public enum BatchStatus { /// /// CSV importato /// Imported = 0, /// /// Nesting richiesto (In corso) /// EstimationRequested, /// /// Nesting Completato /// EstimationDone, /// /// Nesting richiesto (In corso) /// NestRequested, /// /// Nesting Completato /// NestDone, /// /// Nesting approvato /// Approved, /// /// Nesting scartato /// Discarded } /// /// Posizione / Activity degli oggetti Batch /// public enum BatchPosition { /// /// Non iniziato /// NotStarted = 0, /// /// Stack In corso /// StackStarted, /// /// Stack Completato /// StackDone, /// /// Stack currently in LOAD /// Current, /// /// Stack completed /// Completed } public static string PositionStatusDescr(object value) { string answ = ""; try { ComLib.BatchPosition pStatus = (ComLib.BatchPosition)Enum.Parse(typeof(ComLib.BatchPosition), value.ToString()); switch (pStatus) { case BatchPosition.NotStarted: answ = traduci("NotStarted"); break; case BatchPosition.StackStarted: answ = traduci("Stacking"); break; case BatchPosition.StackDone: answ = traduci("StackDone"); break; default: break; } } catch { } return answ; } public static string BatchStatusDescr(object value) { string answ = ""; try { ComLib.BatchStatus bStatus = (ComLib.BatchStatus)Enum.Parse(typeof(ComLib.BatchStatus), value.ToString()); switch (bStatus) { case BatchStatus.Imported: answ = "Imported"; break; case BatchStatus.EstimationRequested: answ = "Estimation Requested"; break; case BatchStatus.EstimationDone: answ = "Estimation Completed"; break; case BatchStatus.NestRequested: answ = "Nesting Requested"; break; case BatchStatus.NestDone: answ = "Nesting Completed"; break; case BatchStatus.Approved: answ = "Nesting Approved"; break; case BatchStatus.Discarded: answ = "Nesting Discarded"; break; default: break; } } catch { } return answ; } /// /// Stati degli oggetti PANEL/SHEET /// public enum PStatus { /// /// Programmato /// Programmed = 0, /// /// Stampa in corso /// Printing, /// /// Stampa completata /// Printed, /// /// Lavorazione in corso /// Machining, /// /// Lavorazione completata /// Machined, /// /// In fase di scaricamento /// Unloading, /// /// Completato / scaricato da macchina (anche su tavola di scarico) /// Unloaded, /// /// Presente / letto su PROD e pronto su scissor lift /// Present } /// /// dati del materiale /// public class MaterialData { /// /// Identificativo univoco del materiale (DA ANAGRAFICA db) /// public int MaterialId { get; set; } /// /// Codice P/N del materiale (cliente) /// public string MaterialPN { get; set; } /// /// Codice P/N del materiale (cliente) /// public string MaterialDescription { get; set; } } /// /// Dati delal lavorazione /// public class WorkData { /// /// Percorso del programma da eseguire /// public string ProgramPath { get; set; } /// /// Data inizio processing /// public DateTime? DtStart { get; set; } /// /// Data fine processing /// public DateTime? DtEnd { get; set; } /// /// Tempo di lavorazione in minuti decimali /// public double WorkTimeMin { get { double answ = 0; if (DtStart != null && DtEnd != null) { try { answ = ((DateTime)DtEnd).Subtract((DateTime)DtStart).TotalMinutes; } catch { } } return answ; } } } /// /// Singolo Pannello da lavorare /// public class Panel { /// /// Identificativo univoco pannello /// public int PanelId { get; set; } /// /// Materiale /// public MaterialData Material { get; set; } /// /// Stato del pannello /// public PStatus Status { get; set; } /// /// Tempi processo x fase printing /// public WorkData Printing { get; set; } /// /// Tempi processo x fase CNC /// public WorkData Machining { get; set; } /// /// Tempi processo x scarico /// public WorkData Unloading { get; set; } } /// /// Classe che rappresenta i BUNK da lavorare /// public class WBunk { /// /// Identificativo univoco stack /// public int StackId { get; set; } /// /// Stato dello Stack di pannelli /// public CStatus Status { get; set; } /// /// Codice dataMAtrix dello stack /// public string DataMatrix { get; set; } /// /// Data inizio processing dello Stack /// public DateTime DtStart { get; set; } /// /// Data inizio processing dello Stack /// public DateTime DtEnd { get; set; } /// /// Elenco dei pannelli(sheets) dello Stack /// public List PanelsList { get; set; } /// /// Numero di Panels da lavorare /// public int NumPanels { get { int answ = 0; try { answ = PanelsList.Count; } catch { } return answ; } } } /// /// Oggetto globale TAKT /// public class Takt { /// /// Codice univoco oggetto TAKT (data.num) /// public string TaktId { get; set; } /// /// Stato del TAKT /// public CStatus Status { get; set; } /// /// Elenco degli Stack da lavorare /// public List StackList { get; set; } /// /// Numero di Stack da lavorare /// public int NumStack { get { int answ = 0; try { answ = StackList.Count; } catch { } return answ; } } } #endregion #region definizione classi impiegate con NEST public class Parte { public int PartId { get; set; } public string DataMatrix { get; set; } public string ExtCode { get; set; } public string Description { get; set; } public int MatId { get; set; } public string PostProc { get; set; } public string ProcessReq { get; set; } public string CadFilePath { get; set; } public int Qty { get; set; } } public class Batch { /// /// ID del batch in esecuzione /// public int BatchId { get; set; } /// /// tempo amssimo eprmesso x nesting (minuti) /// public int maxTime { get; set; } /// /// Tipo di processing richeisto /// public int procType { get; set; } /// /// Codice della amcchina x cui si effettua richeista /// [JsonConverter(typeof(StringEnumConverter))] public mType machineType { get; set; } /// /// Tipo di ordine richeisto /// [JsonConverter(typeof(StringEnumConverter))] public oType orderType { get; set; } } /// /// Salva su redis l'oggetto dei materiali serializzato /// /// public static bool sendMaterials() { bool answ = false; // leggo tab MNATERIALS da DB var table = DataLayer.man.taMat.GetData(); // serializzo string redVal = JsonConvert.SerializeObject(table); // salvo string redKey = $"NKC:SERV:CONF:MATERIALS"; // scrivo per ora solo su REDIS memLayer.ML.setRSV(redKey, redVal); return answ; } /// /// Restituisce il prossimo codice di envelope per comunicare con sistemi esterni /// /// Batch contenuto nell'envelope /// note opzionali (motivo envelope) /// public static string getNextEnv(int BatchID, string note) { // incremento counter long nextIndex = memLayer.ML.setRCntI(redMsgCount); // salvo contenuto della busta string answ = $"Z{nextIndex:000000000000}"; Dictionary lista = new Dictionary(); lista.Add(answ, $"{BatchID}|{note}"); memLayer.ML.redSaveHashDict(redMsgList, lista); // ritorno return answ; } /// /// Restituisce elenco KVP delle buste ancora "pending" (quando processate le elimina da elenco...) /// /// public static KeyValuePair[] getEnvList() { KeyValuePair[] answ = memLayer.ML.redGetHash(redMsgList); return answ; } /// /// Invia una richiesta di esecuzione di Nesting x un Batch /// /// Batch di cui si chiede processing /// note opzionali /// public static bool sendBatchReq(int BatchID, string note) { bool answ = false; // per prima cosa mi serve una "nuova busta" per inviare i messaggi string nextEnv = getNextEnv(BatchID, note); // preparo il contenuto della busta ed invio i messaggi... try { // in base allo stato del BATCH corrente determino il tempo e le opzioni da inviare... var batch = DataLayer.man.taBL.getByKey(BatchID); int mTime = 1; int pType = 1; if (batch[0].STATUS < (int)BatchStatus.EstimationDone) { mTime = memLayer.ML.cdvi("estimMaxTime"); } else { mTime = memLayer.ML.cdvi("nestMaxTime"); pType = 2; } // leggo tab ORDINI da DB var tblOrd = DataLayer.man.taOL.getByBatch(BatchID); // serializzo string redVal = JsonConvert.SerializeObject(tblOrd); // salvo string redKey = $"{redOutPath}:{nextEnv}:ORDERS"; // scrivo su REDIS memLayer.ML.setRSV(redKey, redVal); var tblItm = DataLayer.man.taIL.getByBatch(BatchID); // serializzo redVal = JsonConvert.SerializeObject(tblItm); // salvo redKey = $"{redOutPath}:{nextEnv}:ITEMS"; // scrivo su REDIS memLayer.ML.setRSV(redKey, redVal); // ora versione gerarchica var currBatch = new Batch() { BatchId = BatchID, maxTime = mTime, procType = pType, machineType = mType.Multiax, orderType = oType.BatchRequest }; // serializzo redVal = JsonConvert.SerializeObject(currBatch); // salvo redKey = $"{redOutPath}:{nextEnv}:DATA"; // scrivo su REDIS memLayer.ML.setRSV(redKey, redVal); // invio notifica che c'è una busta da processare redKey = $"{redOutPath}:CURR"; // scrivo su REDIS memLayer.ML.setRSV(redKey, nextEnv); answ = true; } catch { } // restituisco ok return answ; } /// /// Invia una richiesta di esecuzione di CAM x un ordine offline /// /// OfflineOrder di cui si chiede processing /// note opzionali /// public static bool sendOfflineOrderReq(int OffOrderID, string note) { bool answ = false; // per prima cosa mi serve una "nuova busta" per inviare i messaggi string nextEnv = getNextEnv(OffOrderID, note); // preparo il contenuto della busta ed invio i messaggi... try { int mTime = 5; int pType = 1; // serializzo ordini come ARRAY VUOTO string redVal = "[]"; // salvo string redKey = $"{redOutPath}:{nextEnv}:ORDERS"; // scrivo su REDIS memLayer.ML.setRSV(redKey, redVal); // ora ITEMS var tblItm = DataLayer.man.taIL.getByOfflineOrder(OffOrderID); // serializzo redVal = JsonConvert.SerializeObject(tblItm); // salvo redKey = $"{redOutPath}:{nextEnv}:ITEMS"; // scrivo su REDIS memLayer.ML.setRSV(redKey, redVal); // ora versione gerarchica var currBatch = new Batch() { BatchId = OffOrderID, maxTime = mTime, procType = pType, machineType = mType.Offline, orderType = oType.OfflineOrder }; // serializzo redVal = JsonConvert.SerializeObject(currBatch); // salvo redKey = $"{redOutPath}:{nextEnv}:DATA"; // scrivo su REDIS memLayer.ML.setRSV(redKey, redVal); // invio notifica che c'è una busta da processare redKey = $"{redOutPath}:CURR"; // scrivo su REDIS memLayer.ML.setRSV(redKey, nextEnv); answ = true; } catch (Exception exc) { logger.lg.scriviLog($"Eccezione in sendOfflineOrderReq:{Environment.NewLine}{exc}"); } // restituisco ok return answ; } /// /// Verifica lo stato di una richiesta di esecuzione /// /// OfflineOrder di cui si chiede processing /// note opzionali /// public static bool checkOfflineOrderReq(int OffOrderID) { bool answ = false; string typeSearch = "OffOrdCalculation"; string keyEnv = ""; // recupero lista dei vari task APERTI... KeyValuePair[] comAperte = getEnvList(); // processo x cercare ordine... foreach (var item in comAperte) { // cerco dove sia quello richiesto if (item.Value.Contains(typeSearch)) { // controllo SE sia quello richiesto if (item.Value == $"{OffOrderID}|{typeSearch}") { keyEnv = item.Key; break; } } } // controllo e se c'è risposta --> update DB string redKey = $"{redNestAnsw}:FULL:{keyEnv}"; string rawAnsw = memLayer.ML.getRSV(redKey); if (rawAnsw != "") { // cerco risposta come stack --> disegno... var offOrder = deserializeOfflineOrder(rawAnsw); // se ho in risposta 1 stack... if (offOrder.Stacks.Count == 1) { // se ho 1 solo sheet var sheets = offOrder.Stacks[0].Sheets; if (sheets.Count == 1) { // controllo se ho CNC prog answ = sheets[0].MachiningProgram != ""; if (answ) { // aggiorno su DB il disegno... string disegno = sheets[0].Drawing; DataLayer.man.taOffOL.updateDrawing(OffOrderID, disegno); } } } } // restituisco ok return answ; } #endregion #region metodi helper di conversione /// /// Helper x serializzare l'oggetto /// /// /// public static string serializeTakt(Takt currData) { string answ = JsonConvert.SerializeObject(currData); return answ; } /// /// Helper x deserializzare l'oggetto /// /// /// public static Takt deserializeTakt(string rawData) { Takt answ = JsonConvert.DeserializeObject(rawData); return answ; } /// /// Deserializza un ordine offline /// /// /// public static nestReplyOffOrd deserializeOfflineOrder(string rawData) { nestReplyOffOrd answ = null; try { answ = JsonConvert.DeserializeObject(rawData); } catch { } return answ; } #endregion #region metodi x data persistence /// /// Salvo il Takt inviato /// /// Origine del dato: SERV / PROD / NEST /// /// public static bool saveTakt(string origin, Takt currData) { bool answ = false; try { // calcolo valori redis string redKey = $"NKC:{origin.ToUpper()}:TAKT:{currData.TaktId}"; string redVal = serializeTakt(currData); // scrivo per ora solo su REDIS memLayer.ML.setRSV(redKey, redVal); answ = true; } catch { } return answ; } /// /// Leggo il Takt inviato /// /// Origine del dato: SERV / PROD / NEST /// /// public static Takt readTakt(string origin, string TaktId) { Takt answ = null; try { string redKey = $"NKC:{origin.ToUpper()}:TAKT:{TaktId}"; string redVal = memLayer.ML.getRSV(redKey); answ = deserializeTakt(redVal); } catch { } return answ; } #endregion #region metodi per PROD /// /// Fornisce il prossimo TAKT da elaborare oppure null se non ce ne fossero altri da elaborare per la data CORRENTE /// /// public Takt prodGetNextTakt() { return null; } /// /// Chiave primo bunk su redis /// protected static string redFirstBunkKey = $"{redProdReq}:FirstBunk"; /// /// Chiave primo bunk su redis /// protected static string redAllNextBunkKey = $"{redProdReq}:NextBunk"; /// /// Chiave primo bunk su redis /// protected static string redNextBunkKey(int BunkID) { return $"{redProdReq}:NextBunk:{BunkID}"; } /// /// Resetto in REDIS i dati di bunk (corrente e successivi) /// public void resetRedisBunkData() { redisFirstBunk = null; } /// /// Cache redis del PRIMO bunk da lavorare /// private static WBunk redisFirstBunk { get { WBunk answ = null; string rawData = memLayer.ML.getRSV(redFirstBunkKey); if (rawData != "") { // provo a deserializzare try { answ = JsonConvert.DeserializeObject(rawData); } catch { } } return answ; } set { if (value != null) { string redVal = JsonConvert.SerializeObject(value); // default lascio x 5 minuti... memLayer.ML.setRSV(redFirstBunkKey, redVal, 300); } else // se null elimino da redis { memLayer.ML.setRSV(redFirstBunkKey, ""); // elimino TUTTI i next... memLayer.ML.redFlushKey(redAllNextBunkKey); } } } /// /// Recupero da Redis del SUCCESSIVO bunk da lavorare /// /// /// private static WBunk getRedisNextBunk(int currBunkId) { WBunk answ = null; string rawData = memLayer.ML.getRSV(redNextBunkKey(currBunkId)); if (rawData != "") { // provo a deserializzare try { answ = JsonConvert.DeserializeObject(rawData); } catch { } } return answ; } /// /// Salvo in Redis il SUCCESSIVO bunk da lavorare /// /// /// private static void setRedisNextBunk(int currBunkId, WBunk value) { if (value != null) { string redVal = JsonConvert.SerializeObject(value); // default lascio x 5 minuti... memLayer.ML.setRSV(redNextBunkKey(currBunkId), redVal, 300); } else // se null elimino da redis { memLayer.ML.setRSV(redNextBunkKey(currBunkId), ""); } } /// /// Restituisce il PRIMO bunk secondo criterio: /// - posizione = 5 (ho letto da webApp il BUNK e preso in carico) /// - NumSheet > NumSheetUnload /// - Ordinato per StackIndex (crescente) x avere il più VECCHIO /// /// public static WBunk prodGetFirstBunk() { // cerco prima su REDIS... WBunk answ = redisFirstBunk; if (answ == null) { // vado sul DB e leggo ... DS_App.StackListDataTable tabBunks = DataLayer.man.taSTL.getLoaded(); // controllo di averne almeno 1... if (tabBunks.Count > 0) { DS_App.StackListRow currBunk = tabBunks[0]; answ = getBunkFromDb(currBunk); // se ho qualcosa salvo su REDIS redisFirstBunk = answ; } } return answ; } /// /// Restituisce il PROSSIMO bunk secondo criterio: /// - posizione = 5 (ho letto da webApp il BUNK e preso in carico) /// - NumSheet > NumSheetUnload /// - Ordinato per StackIndex (crescente) x avere il più VECCHIO /// - SUCCESSIVO al BunkID(=StackID) ricevuto /// /// public static WBunk prodGetNextBunk(int BunkID) { WBunk answ = getRedisNextBunk(BunkID); if (answ == null) { // vado sul DB e leggo ... DS_App.StackListDataTable tabBunks = DataLayer.man.taSTL.getLoaded(); // controllo di averne almeno 1... if (tabBunks.Count > 0) { DS_App.StackListRow currBunk = null; bool trovato = false; // ciclo foreach (var item in tabBunks) { if (trovato) { currBunk = item; break; } // controllo se sia quello richiesto if (item.StackID == BunkID) { trovato = true; } } // se c'è un bunk trovato --> carico if (currBunk != null) { answ = getBunkFromDb(currBunk); // salvo su redis setRedisNextBunk(BunkID, answ); } } } return answ; } /// /// Recupera e transcodifica DA DB i dati di UN SINGOLO bunk /// /// /// private static WBunk getBunkFromDb(DS_App.StackListRow currBunk) { WBunk answ; // calcolo attributi oggetti CStatus currSt = CStatus.Programmed; if (currBunk.Position == 5) { currSt = CStatus.Running; } else { currSt = CStatus.Done; } List elSheet = new List(); // recupero gli sheets di questo stack... DS_App.SheetListDataTable tabSheets = DataLayer.man.taSHL.getByStack(currBunk.StackID); DateTime dataStart = DateTime.Now; Panel currPanel; foreach (var item in tabSheets) { // converto i workData WorkData wdPrint = new WorkData() { DtStart = item.IsPrntStartNull() ? null : (DateTime?)item.PrntStart, DtEnd = item.IsPrntEndNull() ? null : (DateTime?)item.PrntEnd, ProgramPath = item.PrintFilePath }; WorkData wdMachining = new WorkData() { DtStart = item.IsWrkStartNull() ? null : (DateTime?)item.WrkStart, DtEnd = item.IsWrkEndNull() ? null : (DateTime?)item.WrkEnd, ProgramPath = item.CncFilePath }; WorkData wdUnload = new WorkData() { DtStart = item.IsUnlStartNull() ? null : (DateTime?)item.UnlStart, DtEnd = item.IsUnlEndNull() ? null : (DateTime?)item.UnlEnd }; MaterialData material = new MaterialData() { MaterialId = item.MatID, MaterialDescription = item.MatDesc, MaterialPN = item.MatExtCode.ToString() }; PStatus currPnlStatus = PStatus.Programmed; Enum.TryParse(item.ShStatus.ToString(), out currPnlStatus); currPanel = new Panel() { Printing = wdPrint, Machining = wdMachining, Unloading = wdUnload, Material = material, PanelId = item.StackID, Status = currPnlStatus }; elSheet.Add(currPanel); if (!item.IsPrntStartNull()) { dataStart = item.PrntStart < dataStart ? item.PrntStart : dataStart; } } // compongo il bunk... answ = new WBunk() { StackId = currBunk.StackID, Status = currSt, DataMatrix = currBunk.StackDtmx, PanelsList = elSheet, DtStart = dataStart }; return answ; } /// /// Valore dello Stack correntemente in processing per ML (MachineLoad) /// public static string taktMLCurrStack { get { return memLayer.ML.getRSV(redMLCurrStack); } set { memLayer.ML.setRSV(redMLCurrStack, value); } } #endregion } }