1283 lines
38 KiB
C#
1283 lines
38 KiB
C#
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Converters;
|
|
using NKC_SDK;
|
|
using SteamWare;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace AppData
|
|
{
|
|
/// <summary>
|
|
/// Classe con metodi di supporto per COMUNICAZIONE
|
|
/// </summary>
|
|
public class ComLib
|
|
{
|
|
|
|
/// <summary>
|
|
/// Wrapper traduzione termini
|
|
/// </summary>
|
|
/// <param name="lemma"></param>
|
|
/// <returns></returns>
|
|
public static string traduci(string lemma)
|
|
{
|
|
return user_std.UtSn.Traduci(lemma);
|
|
}
|
|
#region conf posizioni redis
|
|
|
|
public static string redOutPath = "NKC:SERV:BREQ";
|
|
public static string redMsgCount = "NKC:SERV:BREQ:MCount";
|
|
public static string redMsgList = "NKC:SERV:BREQ:MList";
|
|
public static string redMLCurrStack = "NKC:SERV:TAKT:CurrStack";
|
|
|
|
public static string redNestAnsw = "NKC:NEST:BANSW";
|
|
|
|
public static string redProdReq = "NKC:SERV:BUNKS";
|
|
public static string redProdAnsw = "NKC:PROD:BUNKS";
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
#region definizione classi impiegate con PROD
|
|
|
|
|
|
|
|
public static string PositionStatusDescr(object value)
|
|
{
|
|
string answ = "";
|
|
try
|
|
{
|
|
BatchPosition pStatus = (BatchPosition)Enum.Parse(typeof(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
|
|
{
|
|
BatchStatus bStatus = (BatchStatus)Enum.Parse(typeof(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;
|
|
case BatchStatus.Errors:
|
|
answ = "Nesting Impossibile (data errors)";
|
|
break;
|
|
case BatchStatus.PartEval:
|
|
answ = "Part/Item under evaluation";
|
|
break;
|
|
case BatchStatus.PartOk:
|
|
answ = "Part/Item Validated";
|
|
break;
|
|
case BatchStatus.PartKo:
|
|
answ = "Part/Item NOT Validated";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
catch
|
|
{ }
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Oggetto globale TAKT
|
|
/// </summary>
|
|
public class Takt
|
|
{
|
|
/// <summary>
|
|
/// Codice univoco oggetto TAKT (data.num)
|
|
/// </summary>
|
|
public string TaktId { get; set; }
|
|
/// <summary>
|
|
/// Stato del TAKT
|
|
/// </summary>
|
|
public CStatus Status { get; set; }
|
|
/// <summary>
|
|
/// Elenco degli Stack da lavorare
|
|
/// </summary>
|
|
public List<ProdBunk> StackList { get; set; }
|
|
/// <summary>
|
|
/// Numero di Stack da lavorare
|
|
/// </summary>
|
|
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 BatchData
|
|
{
|
|
/// <summary>
|
|
/// ID del batch in esecuzione
|
|
/// </summary>
|
|
public int BatchId { get; set; }
|
|
/// <summary>
|
|
/// tempo amssimo eprmesso x nesting (minuti)
|
|
/// </summary>
|
|
public int maxTime { get; set; }
|
|
/// <summary>
|
|
/// Tipo di processing richiesto
|
|
/// 1 = stima
|
|
/// 2 = nesting
|
|
/// </summary>
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public int procType { get; set; }
|
|
/// <summary>
|
|
/// Codice della amcchina x cui si effettua richeista
|
|
/// </summary>
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public mType machineType { get; set; }
|
|
/// <summary>
|
|
/// Tipo di ordine richeisto
|
|
/// </summary>
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public oType orderType { get; set; }
|
|
}
|
|
/// <summary>
|
|
/// Salva su redis l'oggetto dei materiali serializzato
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
/// <summary>
|
|
/// Restituisce il prossimo codice di envelope per comunicare con sistemi esterni
|
|
/// </summary>
|
|
/// <param name="BatchID">Batch contenuto nell'envelope</param>
|
|
/// <param name="note">note opzionali (motivo envelope)</param>
|
|
/// <returns></returns>
|
|
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<string, string> lista = new Dictionary<string, string>();
|
|
lista.Add(answ, $"{BatchID}|{note}");
|
|
memLayer.ML.redSaveHashDict(redMsgList, lista);
|
|
// ritorno
|
|
return answ;
|
|
}
|
|
/// <summary>
|
|
/// Restituisce elenco KVP delle buste ancora "pending" (quando processate le elimina da elenco...)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static KeyValuePair<string, string>[] getEnvList()
|
|
{
|
|
KeyValuePair<string, string>[] answ = memLayer.ML.redGetHash(redMsgList);
|
|
return answ;
|
|
}
|
|
/// <summary>
|
|
/// resetta la richeista corrente in modo che non ce ne siano altre in coda
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static bool resetBatchReq()
|
|
{
|
|
bool answ = false;
|
|
try
|
|
{
|
|
currBatchReq = "";
|
|
answ = true;
|
|
}
|
|
catch
|
|
{ }
|
|
return answ;
|
|
}
|
|
public static string currBatchReqKey
|
|
{
|
|
get
|
|
{
|
|
return $"{redOutPath}:CURR";
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Valore della richiesta corrente di elaborazione batch
|
|
/// </summary>
|
|
public static string currBatchReq
|
|
{
|
|
get
|
|
{
|
|
return memLayer.ML.getRSV(currBatchReqKey);
|
|
}
|
|
set
|
|
{
|
|
// scrivo su REDIS
|
|
memLayer.ML.setRSV(currBatchReqKey, value);
|
|
}
|
|
}
|
|
public static bool sendFirstValidationBatch()
|
|
{
|
|
bool answ = false;
|
|
// verifico se sia vuota la richeista corrente (= chiuso...)
|
|
|
|
// ora verifico, se ci sono batch in stato 8 (da validare) --> metto in coda!
|
|
var tabBatch = DataLayer.man.taBL.getByStatus(8);
|
|
bool hasValReq = tabBatch.Count > 0;
|
|
if (hasValReq)
|
|
{
|
|
// invia a redis una richiesta...
|
|
ComLib.sendMaterials();
|
|
// recupero PRIMO batchID da validare
|
|
int nextBatchId = 0;
|
|
try
|
|
{
|
|
nextBatchId = tabBatch[0].BatchID;
|
|
}
|
|
catch
|
|
{ }
|
|
if (nextBatchId > 0)
|
|
{
|
|
// richiedo!
|
|
ComLib.sendBatchReq(nextBatchId, "Estimation", 1);
|
|
// registro su DB nesting iniziato... QUANDO MI RISPONDE dovrò verificare che era un abtch x VALIDAZIONE
|
|
DataLayer.man.taBL.updateStatus(nextBatchId, (int)BatchStatus.EstimationRequested, "", 0);
|
|
answ = true;
|
|
}
|
|
}
|
|
// resituisco
|
|
return answ;
|
|
}
|
|
/// <summary>
|
|
/// Invia una richiesta di esecuzione di Nesting x un Batch
|
|
/// </summary>
|
|
/// <param name="BatchID">Batch di cui si chiede processing</param>
|
|
/// <param name="note">note opzionali</param>
|
|
/// <param name="pType">Tipo processo: 1 = stima, 2 = nesting</param>
|
|
/// <returns></returns>
|
|
public static bool sendBatchReq(int BatchID, string note, int pType)
|
|
{
|
|
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;
|
|
if (batch[0].STATUS < (int)BatchStatus.EstimationDone)
|
|
{
|
|
mTime = memLayer.ML.cdvi("estimMaxTime");
|
|
}
|
|
else
|
|
{
|
|
mTime = memLayer.ML.cdvi("nestMaxTime");
|
|
}
|
|
|
|
// init oggetti x fare cicli...
|
|
Order currOrder = null;
|
|
Kit currentKit = null;
|
|
Part currPart = null;
|
|
List<Order> listOrder = new List<Order>();
|
|
List<Kit> listKit = new List<Kit>();
|
|
List<Part> listPart = new List<Part>();
|
|
|
|
// leggo tab ORDINI da DB
|
|
var tblOrd = DataLayer.man.taOL.getByBatch(BatchID);
|
|
// leggo tab KIT da DB
|
|
var tblKit = DataLayer.man.taKL.getByBatch(BatchID);
|
|
// leggo tab ITEMS da DB
|
|
var tblItm = DataLayer.man.taIL.getByBatch(BatchID);
|
|
|
|
// ciclo per comporre oggetto con cicli annidiati ext --> int
|
|
foreach (var rigaOrd in tblOrd)
|
|
{
|
|
listKit = new List<Kit>();
|
|
// compongo kit
|
|
var righeKit = (DS_App.KitListRow[])tblKit.Select($"OrdID = {rigaOrd.OrdID}");
|
|
foreach (var rigaKit in righeKit)
|
|
{
|
|
listPart = new List<Part>();
|
|
// elenco ITEMS
|
|
var righeItems = (DS_App.ItemListRow[])tblItm.Select($"KitID = {rigaKit.KitID}");
|
|
foreach (var rigaItem in righeItems)
|
|
{
|
|
currPart = new Part()
|
|
{
|
|
PartId = rigaItem.ItemID,
|
|
PartExtCode = rigaItem.ItemExtCode,
|
|
PartDtmx = rigaItem.ItemDtmx,
|
|
PartQty = rigaItem.ItemQty,
|
|
MatId = rigaItem.MatID,
|
|
CadFilePath = rigaItem.CadFilePath
|
|
};
|
|
listPart.Add(currPart);
|
|
}
|
|
// compongo KIT
|
|
currentKit = new Kit()
|
|
{
|
|
KitId = rigaKit.KitID,
|
|
KitExtCode = rigaKit.KitExtCode,
|
|
PartList = listPart
|
|
};
|
|
listKit.Add(currentKit);
|
|
}
|
|
// compongo ordine
|
|
currOrder = new Order()
|
|
{
|
|
OrderId = rigaOrd.OrdID,
|
|
OrderCod = rigaOrd.OrdCodOrig,
|
|
OrderExtCode = rigaOrd.OrderExtCode,
|
|
DestPlant = rigaOrd.DestPlant,
|
|
KitList = listKit
|
|
};
|
|
listOrder.Add(currOrder);
|
|
}
|
|
// oggetto complessivo
|
|
batchRequest newBatchreq = new batchRequest()
|
|
{
|
|
BatchId = BatchID,
|
|
EnvNum = nextEnv,
|
|
MaxTime = mTime,
|
|
ProcType = pType,
|
|
MachineType = mType.Multiax,
|
|
OrderType = oType.BatchRequest,
|
|
OrderList = listOrder
|
|
};
|
|
|
|
// serializzo
|
|
string redVal = JsonConvert.SerializeObject(newBatchreq);
|
|
// salvo
|
|
string redKey = $"{redOutPath}:{nextEnv}";
|
|
// scrivo su REDIS
|
|
memLayer.ML.setRSV(redKey, redVal);
|
|
|
|
// invio notifica che c'è una busta da processare
|
|
memLayer.ML.setRSV(currBatchReqKey, nextEnv);
|
|
answ = true;
|
|
}
|
|
catch (Exception exc)
|
|
{ }
|
|
// restituisco ok
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifica lo stato di una richiesta di esecuzione BATCH x stima/nesting
|
|
/// </summary>
|
|
/// <param name="OffOrderID">OfflineOrder di cui si chiede processing</param>
|
|
/// <param name="note">note opzionali</param>
|
|
/// <returns></returns>
|
|
public static bool checkBatchReq(int OffOrderID)
|
|
{
|
|
bool answ = false;
|
|
#if false
|
|
string typeSearch = "OffOrdCalculation";
|
|
string keyEnv = "";
|
|
// recupero lista dei vari task APERTI...
|
|
KeyValuePair<string, string>[] 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].SheetList;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
// restituisco ok
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invia una richiesta di esecuzione di CAM x un ordine offline
|
|
/// </summary>
|
|
/// <param name="OffOrderID">OfflineOrder di cui si chiede processing</param>
|
|
/// <param name="note">note opzionali</param>
|
|
/// <returns></returns>
|
|
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 BatchData()
|
|
{
|
|
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
|
|
memLayer.ML.setRSV(currBatchReqKey, nextEnv);
|
|
answ = true;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
logger.lg.scriviLog($"Eccezione in sendOfflineOrderReq:{Environment.NewLine}{exc}");
|
|
}
|
|
// restituisco ok
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifica lo stato di una richiesta di esecuzione
|
|
/// </summary>
|
|
/// <param name="OffOrderID">OfflineOrder di cui si chiede processing</param>
|
|
/// <param name="note">note opzionali</param>
|
|
/// <returns></returns>
|
|
public static bool checkOfflineOrderReq(int OffOrderID)
|
|
{
|
|
bool answ = false;
|
|
string typeSearch = "OffOrdCalculation";
|
|
string keyEnv = "";
|
|
// recupero lista dei vari task APERTI...
|
|
KeyValuePair<string, string>[] 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+ sheets...
|
|
if (offOrder.SheetList.Count > 0)
|
|
{
|
|
// se ho 1 solo sheet
|
|
var sheets = offOrder.SheetList;
|
|
// 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;
|
|
}
|
|
/// <summary>
|
|
/// Salvo dati su PartList ricevuti da Nesting (COMPETATO)
|
|
/// </summary>
|
|
/// <param name="BatchID"></param>
|
|
/// <param name="BunkList"></param>
|
|
public static void updateBunksFromNesting(int BatchID, List<NestBunk> BunkList)
|
|
{
|
|
// inizio a processare... bunks!
|
|
if (BunkList != null)
|
|
{
|
|
foreach (var bunk in BunkList)
|
|
{
|
|
// creo il BUNK!
|
|
var newBunk = DataLayer.man.taSTL.insertAndReturn(bunk.BunkIndex, BatchID);
|
|
// se ho un bunk in risposta...
|
|
if (newBunk.Count == 1)
|
|
{
|
|
var currBunk = newBunk[0];
|
|
// processo sheets
|
|
if (bunk.SheetList != null)
|
|
{
|
|
foreach (var sheet in bunk.SheetList)
|
|
{
|
|
// creo lo sheet...
|
|
var newSheet = DataLayer.man.taSHL.insertAndReturn(sheet.SheetIndex, sheet.MatId, (decimal)sheet.EstimatedWorktime, currBunk.StackID, sheet.PrintProgram, sheet.MachiningProgram, sheet.Drawing);
|
|
// se ho 1 sheet in risposta
|
|
if (newSheet.Count == 1)
|
|
{
|
|
var currSheet = newSheet[0];
|
|
// processo items!
|
|
if (sheet.PartList != null)
|
|
{
|
|
foreach (var part in sheet.PartList)
|
|
{
|
|
// aggiungo part a sheet (in tab nesting)
|
|
DataLayer.man.taNest.insertAndReturn(currSheet.SheetID, part.PartId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Salvo dati su PartList ricevuti da Nesting (COMPETATO)
|
|
/// </summary>
|
|
/// <param name="BatchID"></param>
|
|
/// <param name="BinList"></param>
|
|
public static void updateBinsFromNesting(int BatchID, List<NestBin> BinList)
|
|
{
|
|
// inizio a processare... bunks!
|
|
if (BinList != null)
|
|
{
|
|
foreach (var item in BinList)
|
|
{
|
|
// creo il BIN!
|
|
var newBin = DataLayer.man.taBN.insertAndReturn(item.BinIndex);
|
|
// se ho un BIN in risposta...
|
|
if (newBin.Count == 1)
|
|
{
|
|
var currBin = newBin[0];
|
|
// processo sheets
|
|
if (item.PartList != null)
|
|
{
|
|
foreach (var part in item.PartList)
|
|
{
|
|
// creo lo sheet...
|
|
var newB2I = DataLayer.man.taBNLS.insertAndReturn(currBin.BinID, part.PartId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Salvo dati su PartList ricevuti da Nesting (COMPETATO)
|
|
/// </summary>
|
|
/// <param name="BatchID"></param>
|
|
/// <param name="CartList"></param>
|
|
public static void updateCartsFromNesting(int BatchID, List<NestCart> CartList)
|
|
{
|
|
// inizio a processare... bunks!
|
|
if (CartList != null)
|
|
{
|
|
foreach (var item in CartList)
|
|
{
|
|
// creo il CART!
|
|
var newCart = DataLayer.man.taCR.insertAndReturn(item.CartIndex);
|
|
// se ho un CART in risposta...
|
|
if (newCart.Count == 1)
|
|
{
|
|
var currCart = newCart[0];
|
|
// processo sheets
|
|
if (item.KitList != null)
|
|
{
|
|
foreach (var kit in item.KitList)
|
|
{
|
|
// creo lo sheet...
|
|
var updKit = DataLayer.man.taKL.updateCart(kit.KitId, currCart.CartID);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Salvo dati su PartList ricevuti da Nesting (stima)
|
|
/// </summary>
|
|
/// <param name="PartList"></param>
|
|
public static void updatePartsFromNesting(List<Part> PartList)
|
|
{
|
|
string PostProcList = "";
|
|
string ProcessesReq = "";
|
|
string pdfFilePath = "";
|
|
// salvo elenco materiali x ogni item...
|
|
foreach (Part currItem in PartList)
|
|
{
|
|
// calcolo parametri...
|
|
PostProcList = ComLib.getPostProcList(currItem.OptParameters);
|
|
ProcessesReq = ComLib.getProcessesReq(currItem.OptParameters);
|
|
pdfFilePath = ComLib.getPdfFilePath(currItem.OptParameters);
|
|
DataLayer.man.taIL.updateFromNesting(currItem.PartId, currItem.MatId, PostProcList, ProcessesReq, pdfFilePath);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recupera le operazioni di PostProcessing, esempio:
|
|
/// - T-Nut
|
|
/// - Round
|
|
/// - Chop
|
|
/// </summary>
|
|
/// <param name="optParameters"></param>
|
|
/// <returns></returns>
|
|
public static string getPostProcList(Dictionary<string, string> optParameters)
|
|
{
|
|
string answ = "";
|
|
foreach (var currOpt in optParameters)
|
|
{
|
|
switch (currOpt.Key)
|
|
{
|
|
case "ChopAtTab":
|
|
case "RoundEdge":
|
|
case "TNutFlag":
|
|
// se contiene YES aggiungo...
|
|
if (currOpt.Value.ToLower() == "yes")
|
|
{
|
|
answ += $"{currOpt.Key}#";
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
// se finisce per "#" trimmo...
|
|
if (answ.EndsWith("#"))
|
|
{
|
|
answ = answ.Substring(0, answ.Length - 1);
|
|
}
|
|
return answ;
|
|
}
|
|
/// <summary>
|
|
/// Recupera le operazioni RICHIESTE a valle
|
|
/// - Paint
|
|
/// - Assembly
|
|
/// - SecOp
|
|
/// </summary>
|
|
/// <param name="optParameters"></param>
|
|
/// <returns></returns>
|
|
public static string getProcessesReq(Dictionary<string, string> optParameters)
|
|
{
|
|
string answ = "";
|
|
foreach (var currOpt in optParameters)
|
|
{
|
|
switch (currOpt.Key)
|
|
{
|
|
case "AssemblyCell":
|
|
case "PaintFlag":
|
|
case "RoundEdge":
|
|
// se contiene YES aggiungo...
|
|
if (currOpt.Value.ToLower() == "yes")
|
|
{
|
|
answ += $"{currOpt.Key}#";
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
// se finisce per "#" trimmo...
|
|
if (answ.EndsWith("#"))
|
|
{
|
|
answ = answ.Substring(0, answ.Length - 1);
|
|
}
|
|
return answ;
|
|
}
|
|
/// <summary>
|
|
/// Recupera file pdf
|
|
/// </summary>
|
|
/// <param name="optParameters"></param>
|
|
/// <returns></returns>
|
|
public static string getPdfFilePath(Dictionary<string, string> optParameters)
|
|
{
|
|
string answ = "";
|
|
if (optParameters.ContainsKey("PdfLink"))
|
|
{
|
|
answ = optParameters["PdfLink"];
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region metodi helper di conversione
|
|
|
|
/// <summary>
|
|
/// Helper x serializzare l'oggetto
|
|
/// </summary>
|
|
/// <param name="currData"></param>
|
|
/// <returns></returns>
|
|
public static string serializeTakt(Takt currData)
|
|
{
|
|
string answ = JsonConvert.SerializeObject(currData);
|
|
return answ;
|
|
}
|
|
/// <summary>
|
|
/// Helper x deserializzare l'oggetto
|
|
/// </summary>
|
|
/// <param name="rawData"></param>
|
|
/// <returns></returns>
|
|
public static Takt deserializeTakt(string rawData)
|
|
{
|
|
Takt answ = JsonConvert.DeserializeObject<Takt>(rawData);
|
|
return answ;
|
|
}
|
|
/// <summary>
|
|
/// Deserializza un ordine offline
|
|
/// </summary>
|
|
/// <param name="rawData"></param>
|
|
/// <returns></returns>
|
|
public static nestReplyOffOrd deserializeOfflineOrder(string rawData)
|
|
{
|
|
nestReplyOffOrd answ = null;
|
|
try
|
|
{
|
|
answ = JsonConvert.DeserializeObject<nestReplyOffOrd>(rawData);
|
|
}
|
|
catch
|
|
{ }
|
|
return answ;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region metodi x data persistence
|
|
|
|
/// <summary>
|
|
/// Salvo il Takt inviato
|
|
/// </summary>
|
|
/// <param name="origin">Origine del dato: SERV / PROD / NEST</param>
|
|
/// <param name="currData"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Leggo il Takt inviato
|
|
/// </summary>
|
|
/// <param name="origin">Origine del dato: SERV / PROD / NEST</param>
|
|
/// <param name="currData"></param>
|
|
/// <returns></returns>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Esegue richieste lettura da PROD
|
|
/// </summary>
|
|
public static void procProdReadReq()
|
|
{
|
|
// !!!FIXME!!!
|
|
}
|
|
/// <summary>
|
|
/// Esegue richieste lettura da PROD
|
|
/// </summary>
|
|
public static void procProdWriteReq()
|
|
{
|
|
// !!!FIXME!!!
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fornisce il prossimo TAKT da elaborare oppure null se non ce ne fossero altri da elaborare per la data CORRENTE
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public Takt prodGetNextTakt()
|
|
{
|
|
return null;
|
|
}
|
|
/// <summary>
|
|
/// Chiave primo bunk su redis
|
|
/// </summary>
|
|
protected static string redFirstBunkKey = $"{redProdReq}:FirstBunk";
|
|
/// <summary>
|
|
/// Chiave primo bunk su redis
|
|
/// </summary>
|
|
protected static string redAllNextBunkKey = $"{redProdReq}:NextBunk";
|
|
/// <summary>
|
|
/// TTL standard x dati da scambiare con PROD (30 gg)
|
|
/// </summary>
|
|
protected static int ttlProdData = 3600 * 24 * 30;
|
|
/// <summary>
|
|
/// Chiave primo bunk su redis
|
|
/// </summary>
|
|
protected static string redNextBunkKey(int BunkID)
|
|
{
|
|
return $"{redProdReq}:NextBunk:{BunkID}";
|
|
}
|
|
/// <summary>
|
|
/// Resetto in REDIS i dati di bunk (corrente e successivi)
|
|
/// </summary>
|
|
public static void resetRedisBunkData()
|
|
{
|
|
redisFirstBunk = null;
|
|
}
|
|
/// <summary>
|
|
/// Cache redis del PRIMO bunk da lavorare
|
|
/// </summary>
|
|
private static ProdBunk redisFirstBunk
|
|
{
|
|
get
|
|
{
|
|
ProdBunk answ = null;
|
|
string rawData = memLayer.ML.getRSV(redFirstBunkKey);
|
|
if (rawData != "")
|
|
{
|
|
// provo a deserializzare
|
|
try
|
|
{
|
|
answ = JsonConvert.DeserializeObject<ProdBunk>(rawData);
|
|
}
|
|
catch
|
|
{ }
|
|
}
|
|
return answ;
|
|
}
|
|
set
|
|
{
|
|
if (value != null)
|
|
{
|
|
string redVal = JsonConvert.SerializeObject(value);
|
|
// default lascio x 5 minuti...
|
|
memLayer.ML.setRSV(redFirstBunkKey, redVal, ttlProdData);
|
|
}
|
|
else
|
|
// se null elimino da redis
|
|
{
|
|
memLayer.ML.setRSV(redFirstBunkKey, "");
|
|
// elimino TUTTI i next...
|
|
memLayer.ML.redFlushKey(redAllNextBunkKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Recupero da Redis del SUCCESSIVO bunk da lavorare
|
|
/// </summary>
|
|
/// <param name="currBunkId"></param>
|
|
/// <returns></returns>
|
|
private static ProdBunk getRedisNextBunk(int currBunkId)
|
|
{
|
|
ProdBunk answ = null;
|
|
string rawData = memLayer.ML.getRSV(redNextBunkKey(currBunkId));
|
|
if (rawData != "")
|
|
{
|
|
// provo a deserializzare
|
|
try
|
|
{
|
|
answ = JsonConvert.DeserializeObject<ProdBunk>(rawData);
|
|
}
|
|
catch
|
|
{ }
|
|
}
|
|
return answ;
|
|
}
|
|
/// <summary>
|
|
/// Salvo in Redis il SUCCESSIVO bunk da lavorare
|
|
/// </summary>
|
|
/// <param name="currBunkId"></param>
|
|
/// <param name="value"></param>
|
|
private static void setRedisNextBunk(int currBunkId, ProdBunk value)
|
|
{
|
|
if (value != null)
|
|
{
|
|
string redVal = JsonConvert.SerializeObject(value);
|
|
// default lascio x 5 minuti...
|
|
memLayer.ML.setRSV(redNextBunkKey(currBunkId), redVal, ttlProdData);
|
|
}
|
|
else
|
|
// se null elimino da redis
|
|
{
|
|
memLayer.ML.setRSV(redNextBunkKey(currBunkId), "");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static ProdBunk prodGetFirstBunk()
|
|
{
|
|
// cerco prima su REDIS...
|
|
ProdBunk 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static ProdBunk prodGetNextBunk(int BunkID)
|
|
{
|
|
ProdBunk 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;
|
|
}
|
|
/// <summary>
|
|
/// Recupera e transcodifica DA DB i dati di UN SINGOLO bunk
|
|
/// </summary>
|
|
/// <param name="currBunk"></param>
|
|
/// <returns></returns>
|
|
private static ProdBunk getBunkFromDb(DS_App.StackListRow currBunk)
|
|
{
|
|
ProdBunk answ;
|
|
// calcolo attributi oggetti
|
|
CStatus currSt = CStatus.Programmed;
|
|
if (currBunk.Position == 5)
|
|
{
|
|
currSt = CStatus.Running;
|
|
}
|
|
else
|
|
{
|
|
currSt = CStatus.Done;
|
|
}
|
|
List<ProdSheet> elSheet = new List<ProdSheet>();
|
|
// recupero gli sheets di questo stack...
|
|
DS_App.SheetListDataTable tabSheets = DataLayer.man.taSHL.getByStack(currBunk.StackID);
|
|
DateTime dataStart = DateTime.Now;
|
|
ProdSheet 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 ProdSheet()
|
|
{
|
|
Printing = wdPrint,
|
|
Machining = wdMachining,
|
|
Unloading = wdUnload,
|
|
Material = material,
|
|
SheetId = item.SheetID,
|
|
Status = currPnlStatus
|
|
};
|
|
elSheet.Add(currPanel);
|
|
if (!item.IsPrntStartNull())
|
|
{
|
|
dataStart = item.PrntStart < dataStart ? item.PrntStart : dataStart;
|
|
}
|
|
}
|
|
// compongo il bunk...
|
|
answ = new ProdBunk()
|
|
{
|
|
BunkId = currBunk.StackID,
|
|
Status = currSt,
|
|
DataMatrix = currBunk.StackDtmx,
|
|
SheetList = elSheet,
|
|
DtStart = dataStart
|
|
};
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recupera e transcodifica DA DB i dati di UN SINGOLO bunk
|
|
/// </summary>
|
|
/// <param name="currBunk"></param>
|
|
/// <returns></returns>
|
|
public static ProdBunk prodGetBunk(int StackID)
|
|
{
|
|
ProdBunk answ = null;
|
|
List<ProdSheet> elSheet = new List<ProdSheet>();
|
|
var tabBunks = DataLayer.man.taSTL.getByKey(StackID);
|
|
if (tabBunks.Count == 1)
|
|
{
|
|
DS_App.StackListRow currBunk = tabBunks[0];
|
|
// calcolo attributi oggetti
|
|
CStatus currSt = CStatus.Programmed;
|
|
if (currBunk.Position == 5)
|
|
{
|
|
currSt = CStatus.Running;
|
|
}
|
|
else
|
|
{
|
|
currSt = CStatus.Done;
|
|
}
|
|
// recupero gli sheets di questo stack...
|
|
DS_App.SheetListDataTable tabSheets = DataLayer.man.taSHL.getByStack(StackID);
|
|
DateTime dataStart = DateTime.Now;
|
|
ProdSheet 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 ProdSheet()
|
|
{
|
|
Printing = wdPrint,
|
|
Machining = wdMachining,
|
|
Unloading = wdUnload,
|
|
Material = material,
|
|
SheetId = item.StackID,
|
|
Status = currPnlStatus
|
|
};
|
|
elSheet.Add(currPanel);
|
|
if (!item.IsPrntStartNull())
|
|
{
|
|
dataStart = item.PrntStart < dataStart ? item.PrntStart : dataStart;
|
|
}
|
|
}
|
|
// compongo il bunk...
|
|
answ = new ProdBunk()
|
|
{
|
|
BunkId = currBunk.StackID,
|
|
Status = currSt,
|
|
DataMatrix = currBunk.StackDtmx,
|
|
SheetList = elSheet,
|
|
DtStart = dataStart
|
|
};
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Valore dello Stack correntemente in processing per ML (MachineLoad)
|
|
/// </summary>
|
|
public static string taktMLCurrStack
|
|
{
|
|
get
|
|
{
|
|
return memLayer.ML.getRSV(redMLCurrStack);
|
|
}
|
|
set
|
|
{
|
|
memLayer.ML.setRSV(redMLCurrStack, value);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|