654 lines
25 KiB
C#
654 lines
25 KiB
C#
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}";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tabella dei batch Descendant di quello corrente
|
|
/// </summary>
|
|
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<int> partListEstim = new List<int>();
|
|
List<int> partListEstimDupl = new List<int>();
|
|
List<int> partListNest = new List<int>();
|
|
List<int> partListNestDupl = new List<int>();
|
|
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<int> materialsList = new List<int>();
|
|
double num = 0;
|
|
double den = 1;
|
|
double currRatio = 0;
|
|
List<double> workRatio = new List<double>();
|
|
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: <b>{avgRatio:P1}</b> (<b>{minRatio:P1}</b> → <b>{maxRatio:P1}</b>) | {materialsList.Count} materials";
|
|
lblProdDet.Text = $"Bunks: <b>{nestAnsw.BunkList.Count}</b> | Sheets: <b>{totSheet}</b> | Carts: <b>{nestAnsw.CartList.Count}</b> | Bins: <b>{nestAnsw.BinList.Count}</b>";
|
|
// 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 = "<hr/>" + sbDebug.Replace("\r\n", "<br/>").ToString();
|
|
}
|
|
}
|
|
if (BatchId > 0)
|
|
{
|
|
checkCreateDescendant();
|
|
showBatchDescendant();
|
|
fixChildBatch();
|
|
}
|
|
}
|
|
get
|
|
{
|
|
int answ = 0;
|
|
int.TryParse(hfBatchId.Value, out answ);
|
|
return answ;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// verifica possibilità avvio TASK x presenza task NON chiusi
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// effettua ribilanciamento ordini partendo da elenco completo e poi assegnando a impianti
|
|
/// </summary>
|
|
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<int, double> RawList = new Dictionary<int, double>();
|
|
Dictionary<int, double> OrderedList = new Dictionary<int, double>();
|
|
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mostro dati dei Batch descendant
|
|
/// </summary>
|
|
private void showBatchDescendant()
|
|
{
|
|
// suddivido
|
|
rebalanceOrder();
|
|
}
|
|
|
|
#endregion Private Methods
|
|
|
|
#region Protected Methods
|
|
|
|
/// <summary>
|
|
/// Cerca il set con lo score migliore calcolando x subset della lista ordinata
|
|
/// </summary>
|
|
/// <param name="OrderedList">Lista ordinata oggetti (INT) + valore</param>
|
|
/// <param name="TargetVal">Valore desiderato (comeSOMMA)</param>
|
|
/// <returns></returns>
|
|
protected Dictionary<int, double> findLocalMin(Dictionary<int, double> OrderedList, double TargetVal)
|
|
{
|
|
Dictionary<int, double> answ = new Dictionary<int, double>();
|
|
List<OrderSetSim> Candidates = new List<OrderSetSim>();
|
|
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<int, double> 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<int, double> 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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Init di base
|
|
/// </summary>
|
|
/// <param name="e"></param>
|
|
protected override void OnInit(EventArgs e)
|
|
{
|
|
checkCreateDescendant();
|
|
}
|
|
|
|
protected void Page_Load(object sender, EventArgs e)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua divisione evitando zeri a den...
|
|
/// </summary>
|
|
/// <param name="num"></param>
|
|
/// <param name="den"></param>
|
|
/// <returns></returns>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Converte il codice stato in effettivo campo
|
|
/// </summary>
|
|
/// <param name="_status"></param>
|
|
/// <returns></returns>
|
|
public string BStatus(object _status)
|
|
{
|
|
string answ = ComLib.BatchStatusDescr(_status);
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Controlla se lo stato sia uguale a quello richiesto
|
|
/// </summary>
|
|
/// <param name="_status"></param>
|
|
/// <param name="statusReq"></param>
|
|
/// <returns></returns>
|
|
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
|
|
}
|
|
} |