using AppData; using NKC_SDK; using SteamWare; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace NKC_WF.WebUserControls { public partial class cmp_batchDetail : BaseUserControl { #region Protected Fields // place master hard-coded protected string PlaceCod = "VIRTNE"; #endregion Protected Fields #region Protected Properties protected double fullTime { get { double answ = 1; if (!string.IsNullOrEmpty(hfFullTime.Value)) { double.TryParse(hfFullTime.Value, out answ); } return answ; } set { hfFullTime.Value = $"{value}"; } } protected int lastValRatio { get { int answ = 50; if (!string.IsNullOrEmpty(hfLastRatio.Value)) { int.TryParse(hfLastRatio.Value, out answ); } return answ; } set { hfLastRatio.Value = $"{value}"; } } protected bool needSave { get { bool answ = true; bool.TryParse(hfNeedSave.Value, out answ); return answ; } set { hfNeedSave.Value = $"{value}"; } } /// /// Tabella dei batch Descendant di quello corrente /// protected DS_App.BatchListDataTable TabBatchDesc { get { return ComLib.BatchDescendant(BatchId); } } protected DS_App.OrderListTreeDataTable TabOrders { get { // cerco in redis return ComLib.OrdersExtByBatch(BatchId); } } protected int valRatio { get { int answ = 50; if (!string.IsNullOrEmpty(txtRatio.Text)) { int.TryParse(txtRatio.Text, out answ); } return answ; } set { txtRatio.Text = $"{value}"; // scrivo ANCHE i valori assoluti lblRatio01.Text = $"{valRatio} %"; lblRatio02.Text = $"{100 - valRatio} %"; lblTime01.Text = $"{fullTime * value / (100 * 60):N2} h"; lblTime02.Text = $"{fullTime * (100 - value) / (100 * 60):N2} h"; } } #endregion Protected Properties #region Public Properties public int BatchId { set { hfBatchId.Value = value.ToString(); frmView.DataBind(); lblMatDet.Text = ""; lblProdDet.Text = ""; if (memLayer.ML.CRB("enableMongo")) { // cerco da lista salvataggi Estim/Nest... var estimAnsw = ComLib.man.getEstAnsw(value); var nestAnsw = ComLib.man.getNestAnsw(value); StringBuilder sbDebug = new StringBuilder(); sbDebug.AppendLine("Debug Info:"); // elenchi x ricerca duplicati List partListEstim = new List(); List partListEstimDupl = new List(); List partListNest = new List(); List partListNestDupl = new List(); if (estimAnsw != null) { try { foreach (var part in estimAnsw.PartList) { if (partListEstim.Contains(part.PartId)) { partListEstimDupl.Add(part.PartId); } else { partListEstim.Add(part.PartId); } } } catch { } try { sbDebug.AppendLine($"ESTIM: EnvNum: {estimAnsw.EnvNum} | Worktime: {estimAnsw.EstimatedWorktime / 60:N2} min | Processing Runtime {estimAnsw.ProcessingRuntime / 60:N2} min | Parts #: {estimAnsw.PartList.Count} | Distinct Part # {partListEstim.Count}"); // se ho duplicati indico: if (partListEstimDupl.Count > 0) { sbDebug.AppendLine("---------------------"); sbDebug.AppendLine($"ESTIM: FOUND {partListEstimDupl.Count} duplicate:"); foreach (var partId in partListEstimDupl) { sbDebug.AppendLine($"{partId}"); } sbDebug.AppendLine("---------------------"); } } catch { } } if (nestAnsw != null) { try { if (nestAnsw.BunkList != null) { foreach (var bunk in nestAnsw.BunkList) { foreach (var sheet in bunk.SheetList) { foreach (var part in sheet.PartList) { if (partListNest.Contains(part.PartId)) { partListNestDupl.Add(part.PartId); } else { partListNest.Add(part.PartId); } } } } } } catch { } int totKit = 0; try { if (nestAnsw.CartList != null) { foreach (var cart in nestAnsw.CartList) { totKit += cart.KitList.Count; } } } catch { } //il tot delle part è in bunk > Sheet > part int totPartNum = 0; int totSheet = 0; List materialsList = new List(); double num = 0; double den = 1; double currRatio = 0; List workRatio = new List(); try { if (nestAnsw.BunkList != null) { foreach (var bunk in nestAnsw.BunkList) { totSheet += bunk.SheetList.Count; foreach (var sheet in bunk.SheetList) { totPartNum += sheet.PartList.Count; num = sheet.SurfaceWork > 0 ? sheet.SurfaceWork : 0; den = sheet.SurfaceTotal > 0 ? sheet.SurfaceTotal : 1; currRatio = ratioProt(num, den); workRatio.Add(currRatio); if (!materialsList.Contains(sheet.MatId)) { materialsList.Add(sheet.MatId); } } } } } catch { } try { // ordino le medie workRatio.Sort(); // elimino le + basse quanti materiali ci sono... workRatio.RemoveRange(0, materialsList.Count); double avgRatio = workRatio.Average(); double minRatio = workRatio.Min(); double maxRatio = workRatio.Max(); sbDebug.AppendLine($"NEST: EnvNum: {nestAnsw.EnvNum} | Worktime: {nestAnsw.EstimatedWorktime / 60:N2} min | Processing Runtime {nestAnsw.ProcessingRuntime / 60:N2} min"); lblMatDet.Text = $"avg: {avgRatio:P1} ({minRatio:P1}{maxRatio:P1}) | {materialsList.Count} materials"; lblProdDet.Text = $"Bunks: {nestAnsw.BunkList.Count} | Sheets: {totSheet} | Carts: {nestAnsw.CartList.Count} | Bins: {nestAnsw.BinList.Count}"; // se ho duplicati indico: if (partListNestDupl.Count > 0) { sbDebug.AppendLine("---------------------"); sbDebug.AppendLine($"NEST: FOUND {partListNestDupl.Count} duplicate:"); foreach (var partId in partListNestDupl) { sbDebug.AppendLine($"{partId}"); } sbDebug.AppendLine("---------------------"); } // s enon corrispondono if (partListEstim.Count != partListNest.Count) { sbDebug.AppendLine("---------------------"); if (partListEstim.Count > partListNest.Count) { sbDebug.AppendLine($"EST OK | NEST missing:"); foreach (var partId in partListEstim) { if (!partListNest.Contains(partId)) { sbDebug.AppendLine($"{partId}"); } } } else { sbDebug.AppendLine($"EST missing | NEST OK:"); foreach (var partId in partListNest) { if (!partListEstim.Contains(partId)) { sbDebug.AppendLine($"{partId}"); } } } sbDebug.AppendLine("---------------------"); } } catch { } } if (memLayer.ML.CRS("environment") == "DEV") { lblTestJson.Text = "
" + sbDebug.Replace("\r\n", "
").ToString(); } } if (BatchId > 0) { checkCreateDescendant(); showBatchDescendant(); fixChildBatch(); } } get { int answ = 0; int.TryParse(hfBatchId.Value, out answ); return answ; } } /// /// verifica possibilità avvio TASK x presenza task NON chiusi /// /// public bool canStartNew { get { return ComLib.canStartNew; } } #endregion Public Properties #region Private Methods private void checkCreateDescendant() { // se ho un batchId... if (BatchId != 0) { // verifico se ho descendant if (TabBatchDesc.Rows.Count == 0) { // altrimenti creo... DLMan.taBL.makeDescendantByKey(BatchId, PlaceCod); } } } private void fixChildBatch() { DS_App.BatchListDataTable TabBatch = TabBatchDesc; cmp_orderExtListNE01.BatchId = TabBatch.Rows.Count > 0 ? TabBatchDesc[0].BatchID : 0; cmp_orderExtListNE02.BatchId = TabBatch.Rows.Count > 0 ? TabBatchDesc[1].BatchID : 0; cmp_orderExtListNE01.doUpdate(); cmp_orderExtListNE02.doUpdate(); } private void fixRatio() { // sistemazione ratio calcolata... DS_App.OrderListTreeDataTable tabNe01 = DLMan.taOLT.getByBatch(cmp_orderExtListNE01.BatchId); DS_App.OrderListTreeDataTable tabNe02 = DLMan.taOLT.getByBatch(cmp_orderExtListNE02.BatchId); double totTime01 = tabNe01.Sum(x => x.EstProcTime); double totTime02 = tabNe02.Sum(x => x.EstProcTime); // aggiorno valore ratio e ratio last... fullTime = totTime01 + totTime02; fullTime = fullTime > 0 ? fullTime : 1; double newRatio = (totTime01 * 100 / fullTime); valRatio = (int)newRatio; lastValRatio = valRatio; } /// /// effettua ribilanciamento ordini partendo da elenco completo e poi assegnando a impianti /// private void rebalanceOrder() { // solo se variato il ratio... if (valRatio != lastValRatio) { // costruisco vettore durata ordini OrderSetSim FullSet = new OrderSetSim(); OrderSetSim SetNe01 = new OrderSetSim(); OrderSetSim SetNe02 = new OrderSetSim(); // creo liste x i valori da processare... Dictionary RawList = new Dictionary(); Dictionary OrderedList = new Dictionary(); foreach (var item in TabOrders) { RawList.Add(item.OrdID, item.EstProcTime); } // ordino la lista x processing successivo OrderedList = RawList.OrderBy(x => x.Value).ToDictionary(t => t.Key, t => t.Value); // salvo i set FullSet.OrderSet = OrderedList; // lavoro sull'ottimizzare il MINORE double ValRatio = (double)valRatio / 100; if (ValRatio <= 0.5) { SetNe01.TargetValue = FullSet.ActualValue * ValRatio; SetNe01.OrderSet = findLocalMin(OrderedList, SetNe01.TargetValue); // l'altro è la differenza SetNe02.OrderSet = OrderedList; foreach (var item in SetNe01.OrderSet) { SetNe02.OrderSet.Remove(item.Key); } } else { SetNe02.TargetValue = FullSet.ActualValue * (1 - ValRatio); SetNe02.OrderSet = findLocalMin(OrderedList, SetNe02.TargetValue); // l'altro è la differenza SetNe01.OrderSet = OrderedList; foreach (var item in SetNe02.OrderSet) { SetNe01.OrderSet.Remove(item.Key); } } // riorganizzo tabOrders... foreach (var orderItem in TabOrders) { if (SetNe01.OrderSet.ContainsKey(orderItem.OrdID)) { if (orderItem.BatchID != cmp_orderExtListNE01.BatchId) { DLMan.taOLT.updateBatch(orderItem.BatchID, orderItem.OrdID, cmp_orderExtListNE01.BatchId); } } else { if (orderItem.BatchID != cmp_orderExtListNE02.BatchId) { DLMan.taOLT.updateBatch(orderItem.BatchID, orderItem.OrdID, cmp_orderExtListNE02.BatchId); } } } // --> richiede salvataggio needSave = true; } fixRatio(); } /// /// Mostro dati dei Batch descendant /// private void showBatchDescendant() { // suddivido rebalanceOrder(); } #endregion Private Methods #region Protected Methods /// /// Cerca il set con lo score migliore calcolando x subset della lista ordinata /// /// Lista ordinata oggetti (INT) + valore /// Valore desiderato (comeSOMMA) /// protected Dictionary findLocalMin(Dictionary OrderedList, double TargetVal) { Dictionary answ = new Dictionary(); List Candidates = new List(); OrderSetSim CurrSimSet = new OrderSetSim(); // parte dal valore (singolo) più piccolo tra quelli maggiori del target... se c'è... var OrdSup = OrderedList.Where(x => x.Value > TargetVal).OrderBy(x => x.Value); if (OrdSup != null && OrdSup.Any()) { var currOrder = OrdSup.FirstOrDefault(); CurrSimSet = new OrderSetSim(); CurrSimSet.TargetValue = TargetVal; CurrSimSet.OrderSet.Add(currOrder.Key, currOrder.Value); Candidates.Add(CurrSimSet); } // ora guardo gli elementi restanti.. se ci sono var OrdInf = OrderedList.Where(x => x.Value <= TargetVal).OrderByDescending(x => x.Value).ToDictionary(t => t.Key, t => t.Value); if (OrdInf != null && OrdInf.Any()) { var currOrder = OrdInf.FirstOrDefault(); CurrSimSet = new OrderSetSim(); CurrSimSet.TargetValue = TargetVal; CurrSimSet.OrderSet.Add(currOrder.Key, currOrder.Value); Candidates.Add(CurrSimSet); // prendo i restanti tranne il primo OrdInf.Remove(currOrder.Key); // se rimane qualcosa... if (OrdInf != null && OrdInf.Any()) { // calcolo il minimo locale nei 2 casi, da soli Dictionary TestOrderSet01 = findLocalMin(OrdInf, TargetVal); if (TestOrderSet01.Count > 0) { OrderSetSim CurrSet = new OrderSetSim(); CurrSet.TargetValue = TargetVal; CurrSet.OrderSet = TestOrderSet01; Candidates.Add(CurrSet); } // ...e con il primo valore... Dictionary TestOrderSet02 = findLocalMin(OrdInf, TargetVal - currOrder.Value); TestOrderSet02.Add(currOrder.Key, currOrder.Value); // verifico se migliorativo... if (TestOrderSet02.Count > 0) { OrderSetSim CurrSet = new OrderSetSim(); CurrSet.TargetValue = TargetVal - currOrder.Value; CurrSet.OrderSet = TestOrderSet02; Candidates.Add(CurrSet); } } } // cerco il minimo... if (Candidates != null && Candidates.Count > 0) { answ = Candidates.OrderBy(x => x.IndexScore).FirstOrDefault().OrderSet; } // calcolo il minimo e lo restituisco... return answ; } protected void lbtAccept_Click(object sender, EventArgs e) { // registro accettazione Nesting DLMan.taBL.acceptBatch(BatchId, DLMan.CodSoggCurrUser); raiseEvent(); } protected void lbtSendEstim_Click(object sender, EventArgs e) { // invia a redis una richiesta... ComLib.sendMaterials(); ComLib.sendBatchReq(BatchId, "Estimation", 3, false); // registro su DB nesting iniziato... DLMan.taBL.updateStatus(BatchId, (int)BatchStatus.EstimationRequested, "", 0); raiseEvent(); } protected void lbtSendNesting_Click(object sender, EventArgs e) { // invia a redis a a richiesta... ComLib.sendMaterials(); ComLib.sendBatchReq(BatchId, "Nesting", 2, false); // registro su DB nesting iniziato... DLMan.taBL.updateStatus(BatchId, (int)BatchStatus.NestRequested, "", -1); raiseEvent(); } protected void lbtStopEstim_Click(object sender, EventArgs e) { // resetto richiesta ComLib.resetBatchReq(); // registro su DB nesting iniziato... DLMan.taBL.updateStatus(BatchId, (int)BatchStatus.Imported, "", -1); raiseEvent(); } protected void lbtStopNesting_Click(object sender, EventArgs e) { // resetto richiesta ComLib.resetBatchReq(); // registro su DB nesting iniziato... DLMan.taBL.updateStatus(BatchId, (int)BatchStatus.EstimationDone, "", -1); raiseEvent(); } /// /// Init di base /// /// protected override void OnInit(EventArgs e) { checkCreateDescendant(); } protected void Page_Load(object sender, EventArgs e) { } /// /// Effettua divisione evitando zeri a den... /// /// /// /// protected double ratioProt(double num, double den) { den = den == 0 ? 1 : den; return num / den; } protected void txtRatio_TextChanged(object sender, EventArgs e) { rebalanceOrder(); fixChildBatch(); } #endregion Protected Methods #region Internal Methods internal void doUpdate() { frmView.DataBind(); } #endregion Internal Methods #region Public Methods /// /// Converte il codice stato in effettivo campo /// /// /// public string BStatus(object _status) { string answ = ComLib.BatchStatusDescr(_status); return answ; } /// /// Controlla se lo stato sia uguale a quello richiesto /// /// /// /// public bool checkStatus(object _status, BatchStatus statusReq) { bool answ = false; int status = -1; int.TryParse(_status.ToString(), out status); answ = (status == (int)statusReq); return answ; } #endregion Public Methods } }