using AppData; using NKC_SDK; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace NKC_WF.WebUserControls { public partial class cmp_batchDetailSplit : BaseUserControl { #region Protected Properties protected bool BatchIsAncestor { get { return ComLib.BType(BatchId) == BatchType.Ancestor; } } /// /// Status corrente del batch /// protected BatchStatus CurrBatchStatus { get { return ComLib.BStatus(BatchId); } } 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 lastRatio01 { get { int answ = 50; if (!string.IsNullOrEmpty(hfLastRatio01.Value)) { int.TryParse(hfLastRatio01.Value, out answ); } return answ; } set { hfLastRatio01.Value = $"{value}"; } } protected int lastRatio02 { get { int answ = 50; if (!string.IsNullOrEmpty(hfLastRatio02.Value)) { int.TryParse(hfLastRatio02.Value, out answ); } return answ; } set { hfLastRatio02.Value = $"{value}"; } } protected int lastRatio03 { get { int answ = 50; if (!string.IsNullOrEmpty(hfLastRatio03.Value)) { int.TryParse(hfLastRatio03.Value, out answ); } return answ; } set { hfLastRatio03.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 double totTime01 { get { double answ = 0; DS_App.OrderListTreeDataTable tabNe01 = DLMan.taOLT.getByBatch(cmp_orderExtListNE01.BatchId); answ = tabNe01.Sum(x => x.EstProcTime); return answ; } } protected double totTime02 { get { double answ = 0; DS_App.OrderListTreeDataTable tabNe02 = DLMan.taOLT.getByBatch(cmp_orderExtListNE02.BatchId); answ = tabNe02.Sum(x => x.EstProcTime); return answ; } } protected double totTime03 { get { double answ = 0; DS_App.OrderListTreeDataTable tabNe03 = DLMan.taOLT.getByBatch(cmp_orderExtListNE03.BatchId); answ = tabNe03.Sum(x => x.EstProcTime); return answ; } } #if false 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 = $"{100 - valRatio} %"; lblRatio02.Text = $"{valRatio} %"; lblTime01.Text = $"{fullTime * (100 - value) / (100 * 60):N2} h"; lblTime02.Text = $"{fullTime * value / (100 * 60):N2} h"; } } #endif #endregion Protected Properties #region Public Properties public int BatchId { set { hfBatchId.Value = value.ToString(); if (BatchId > 0) { checkCreateDescendant(); doUpdate(); } fixRatio(); } get { int answ = 0; int.TryParse(hfBatchId.Value, out answ); return answ; } } public bool isSplitted { get { bool answ = false; bool.TryParse(hfIsSplit.Value, out answ); return answ; } set { hfIsSplit.Value = $"{value}"; } } #endregion Public Properties protected int Ratio01 = 0; protected int Ratio02 = 0; protected int Ratio03 = 0; #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); } } } /// /// verifico modalità display (editing solo se PRIMA di lanciare nesting...) /// private void checkDisplayMode() { #if false txtRatio.Visible = (BatchIsAncestor && CurrBatchStatus <= BatchStatus.EstimationDone); #endif var splitEnab = (BatchIsAncestor && CurrBatchStatus <= BatchStatus.EstimationDone); // se non suddiviso --> indico rosso! double tmpTime = totTime01 + totTime02 + totTime03; string baseCssClass = "btn btn-success btn-block"; if (tmpTime < 0.1) { // imposto colore btn ROSSO... baseCssClass = "btn btn-danger btn-block"; isSplitted = false; } else { isSplitted = true; } lbtBalance.CssClass = splitEnab ? baseCssClass : baseCssClass + " disabled"; cmp_orderExtListNE01.Visible = splitEnab; cmp_orderExtListNE02.Visible = splitEnab; cmp_orderExtListNE03.Visible = splitEnab; cmp_batchDetailSplitInfoNE01.Visible = !splitEnab; cmp_batchDetailSplitInfoNE02.Visible = !splitEnab; cmp_batchDetailSplitInfoNE03.Visible = !splitEnab; } private void Cmp_orderExtListNE01_eh_doRefresh(object sender, EventArgs e) { // sposto ordine in altro set DLMan.taOLT.updateBatch(cmp_orderExtListNE01.BatchId, cmp_orderExtListNE01.SelOrderId, cmp_orderExtListNE02.BatchId); fixChildBatch(); fixRatio(); } private void Cmp_orderExtListNE02_eh_doRefresh(object sender, EventArgs e) { // sposto ordine in altro set DLMan.taOLT.updateBatch(cmp_orderExtListNE02.BatchId, cmp_orderExtListNE02.SelOrderId, cmp_orderExtListNE01.BatchId); fixChildBatch(); fixRatio(); } private void Cmp_orderExtListNE03_eh_doRefresh(object sender, EventArgs e) { // sposto ordine in altro set DLMan.taOLT.updateBatch(cmp_orderExtListNE03.BatchId, cmp_orderExtListNE03.SelOrderId, cmp_orderExtListNE01.BatchId); fixChildBatch(); fixRatio(); } private void fixChildBatch() { // sistemo dati ordini DS_App.BatchListDataTable TabBatch = TabBatchDesc; cmp_orderExtListNE01.BatchId = TabBatch.Rows.Count > 0 ? TabBatchDesc[0].BatchID : 0; cmp_orderExtListNE02.BatchId = TabBatch.Rows.Count > 1 ? TabBatchDesc[1].BatchID : 0; cmp_orderExtListNE03.BatchId = TabBatch.Rows.Count > 2 ? TabBatchDesc[2].BatchID : 0; cmp_orderExtListNE01.doUpdate(); cmp_orderExtListNE02.doUpdate(); cmp_orderExtListNE03.doUpdate(); cmp_batchDetailSplitInfoNE01.BatchId = cmp_orderExtListNE01.BatchId; cmp_batchDetailSplitInfoNE02.BatchId = cmp_orderExtListNE02.BatchId; cmp_batchDetailSplitInfoNE03.BatchId = cmp_orderExtListNE03.BatchId; } private void fixRatio() { // calcolo tempi.... double tmpTime = totTime01 + totTime02 + totTime03; fullTime = tmpTime > 0 ? tmpTime : 1; // sistemazione ratio calcolate... Ratio01 = (int)(totTime01 * 100 / fullTime); Ratio02 = (int)(totTime02 * 100 / fullTime); Ratio03 = (int)(totTime03 * 100 / fullTime); lastRatio01 = Ratio01; lastRatio02 = Ratio02; lastRatio03 = Ratio03; #if false double newRatio = (totTime02 * 100 / fullTime); valRatio = (int)newRatio; lastValRatio = valRatio; #endif } /// /// effettua ribilanciamento ordini partendo da elenco completo e poi assegnando a impianti /// private void rebalanceOrder() { // solo se variato il ratio... if (Ratio01 != lastRatio01 || Ratio02 != lastRatio02 || Ratio03 != lastRatio03) { Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); // costruisco vettore durata ordini OrderSetSim FullSet = new OrderSetSim(); OrderSetSim SetNe01 = new OrderSetSim(); OrderSetSim SetNe02 = new OrderSetSim(); OrderSetSim SetNe03 = 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); // imposto maxDepth ... int maxDepth = 8; // salvo i set FullSet.OrderSet = OrderedList; // FIXME TODO !!! valutare se partire dal +m piccolo al + grande... // ciclo x riempire il + primo... SetNe01.TargetValue = FullSet.ActualValue * (double)Ratio01/100; SetNe02.TargetValue = FullSet.ActualValue * (double)Ratio02 / 100; SetNe03.TargetValue = FullSet.ActualValue * (double)Ratio03 / 100; SetNe01.OrderSet = findLocalMin(OrderedList, SetNe01.TargetValue, maxDepth); // calcolo il restante per differenza... var ordRemain = OrderedList; foreach (var item in SetNe01.OrderSet) { ordRemain.Remove(item.Key); } SetNe02.OrderSet = findLocalMin(ordRemain, SetNe02.TargetValue, maxDepth); // l'ultimo è la differenza SetNe03.OrderSet = ordRemain; foreach (var item in SetNe02.OrderSet) { SetNe03.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 (SetNe02.OrderSet.ContainsKey(orderItem.OrdID)) { if (orderItem.BatchID != cmp_orderExtListNE02.BatchId) { DLMan.taOLT.updateBatch(orderItem.BatchID, orderItem.OrdID, cmp_orderExtListNE02.BatchId); } } else { if (orderItem.BatchID != cmp_orderExtListNE03.BatchId) { DLMan.taOLT.updateBatch(orderItem.BatchID, orderItem.OrdID, cmp_orderExtListNE03.BatchId); } } } // --> richiede salvataggio needSave = true; // salvo tempo calcolo stopWatch.Stop(); Log.Instance.Info($"Rebalance executed | R01: {Ratio01} | R02: {Ratio02} | R03: {Ratio03} | maxDepth: {maxDepth} | elapsed ms: {stopWatch.ElapsedMilliseconds}"); } fixRatio(); } #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 (come SOMMA) /// Massima profondità ricorsione accettata (x evitare loop infinito) /// protected Dictionary findLocalMin(Dictionary OrderedList, double TargetVal, int maxDepth) { 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); // guardo i successivi che NON superano + corrente... Dictionary TestOrderSetMinDelta = findStepMin(OrdInf, TargetVal - currOrder.Value); TestOrderSetMinDelta.Add(currOrder.Key, currOrder.Value); // verifico se migliorativo... if (TestOrderSetMinDelta.Count > 0) { OrderSetSim CurrSet = new OrderSetSim(); CurrSet.TargetValue = TargetVal - currOrder.Value; CurrSet.OrderSet = TestOrderSetMinDelta; Candidates.Add(CurrSet); } // solo successivi che non superano Dictionary TestOrderSetMin = findStepMin(OrdInf, TargetVal); // verifico se migliorativo... if (TestOrderSetMin.Count > 0) { OrderSetSim CurrSet = new OrderSetSim(); CurrSet.TargetValue = TargetVal - currOrder.Value; CurrSet.OrderSet = TestOrderSetMin; Candidates.Add(CurrSet); } // se posso fare ricorsioni if (maxDepth > 0) { maxDepth--; // se rimane qualcosa... if (OrdInf != null && OrdInf.Any()) { // calcolo il minimo locale nei 2 casi, da soli Dictionary TestOrderSet01 = findLocalMin(OrdInf, TargetVal, maxDepth); 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, maxDepth); 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; } /// /// Cerca il set della lista ordinata <= target val /// /// Lista ordinata oggetti (INT) + valore /// Valore desiderato (come SOMMA) /// protected Dictionary findStepMin(Dictionary OrderedList, double TargetVal) { double CurrVal = 0; Dictionary answ = new Dictionary(); foreach (var item in OrderedList) { if (item.Value <= TargetVal-CurrVal) { CurrVal += item.Value; answ.Add(item.Key, item.Value); } } // restituisco set minore... return answ; } protected void lbtBalance_Click(object sender, EventArgs e) { //valRatio = 51; //lastValRatio = 0; Ratio01 = 33; Ratio02 = 33; Ratio03 = 34; lastRatio01 = 0; lastRatio02 = 0; lastRatio03 = 0; doUpdate(); } private int numMach = 3; protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { // verifica visualizzazione fixChildBatch(); fixRatio(); checkDisplayMode(); } cmp_orderExtListNE01.eh_doRefresh += Cmp_orderExtListNE01_eh_doRefresh; cmp_orderExtListNE02.eh_doRefresh += Cmp_orderExtListNE02_eh_doRefresh; cmp_orderExtListNE03.eh_doRefresh += Cmp_orderExtListNE03_eh_doRefresh; } #if false protected void txtRatio_TextChanged(object sender, EventArgs e) { doUpdate(); } #endif #endregion Protected Methods #region Public Methods public void doUpdate() { rebalanceOrder(); fixChildBatch(); checkDisplayMode(); raiseReset(); } #endregion Public Methods } }