Completato metodo set EXt + reset assegnazioni!

This commit is contained in:
Samuele Locatelli
2025-12-24 17:56:27 +01:00
parent e17d28fdd3
commit 1c2ac0cbb5
14 changed files with 343 additions and 76 deletions
+1 -7
View File
@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Core.Generic
namespace EgwCoreLib.Lux.Core.Generic
{
/// <summary>
/// Definizione richiesta di bilanciamento per il calcolo
@@ -0,0 +1,17 @@
namespace EgwCoreLib.Lux.Core.Generic
{
public class DirectAssignReqDto
{
#region Public Properties
/// <summary>
/// Dizionario associazioni Part + estimTime
/// </summary>
public Dictionary<string, double> DictPartAssign { get; set; } = new Dictionary<string, double>();
public int OrderRowID { get; set; } = 0;
public int ProdAssignID { get; set; } = 0;
public List<string> TagsList { get; set; } = new List<string>();
#endregion Public Properties
}
}
@@ -59,14 +59,6 @@ namespace EgwCoreLib.Lux.Core.RestPayload
#region Public Properties
/// <summary>
/// Elenco Macchine in formato stringa join
/// </summary>
public string ListMachinesJoin
{
get => string.Join(", ", machineCalcResults.Select(x => x.Name).OrderBy(x => x).ToList());
}
/// <summary>
/// Elenco effettivo macchine
/// </summary>
@@ -75,6 +67,14 @@ namespace EgwCoreLib.Lux.Core.RestPayload
get => machineCalcResults.Select(x => x.Name).OrderBy(x => x).ToList();
}
/// <summary>
/// Elenco Macchine in formato stringa join
/// </summary>
public string ListMachinesJoin
{
get => string.Join(", ", machineCalcResults.Select(x => x.Name).OrderBy(x => x).ToList());
}
/// <summary>
/// Elenco delle etichette parts non OK
/// </summary>
@@ -83,13 +83,6 @@ namespace EgwCoreLib.Lux.Core.RestPayload
get => listUnWorkable ?? new List<string>();
}
/// <summary>
/// Elenco etichette parts OK
/// </summary>
public List<string> ListWorkable
{
get => listWorkable ?? new List<string>();
}
/// <summary>
/// Elenco parts vincolate (=non ogni impianto)
/// </summary>
@@ -98,6 +91,14 @@ namespace EgwCoreLib.Lux.Core.RestPayload
get => listVincolated ?? new List<string>();
}
/// <summary>
/// Elenco etichette parts OK
/// </summary>
public List<string> ListWorkable
{
get => listWorkable ?? new List<string>();
}
/// <summary>
/// Dettaglio combinazioni carico di lavoro
/// </summary>
@@ -140,6 +141,11 @@ namespace EgwCoreLib.Lux.Core.RestPayload
get => numOkVin;
}
public int NumParts
{
get => listComplete?.Count() ?? 0;
}
/// <summary>
/// Tempo massimo complessivo in ORE
/// </summary>
@@ -173,8 +179,8 @@ namespace EgwCoreLib.Lux.Core.RestPayload
private List<string>? listComplete = null;
private List<string>? listUnWorkable = null;
private List<string>? listWorkable = null;
private List<string>? listVincolated = null;
private List<string>? listWorkable = null;
private List<MachineCalcResultDTO> machineCalcResults = new List<MachineCalcResultDTO>();
private int numKo = 0;
private int numOk = 0;
@@ -12,7 +12,6 @@ using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using NLog;
using System.Globalization;
using System.Linq;
using static EgwCoreLib.Lux.Core.Enums;
namespace EgwCoreLib.Lux.Data.Controllers
@@ -3367,6 +3366,34 @@ namespace EgwCoreLib.Lux.Data.Controllers
return dbResult;
}
/// <summary>
/// Reset impostazione ProdItem x ripartire senza settings
/// </summary>
/// <param name="orderRowID"></param>
/// <returns></returns>
internal async Task<int> ProdItemResetOrderAssign(int orderRowID)
{
int numItem = 0;
//using (DataLayerContext dbCtx = new DataLayerContext(_config))
using (DataLayerContext dbCtx = new DataLayerContext())
{
try
{
numItem = await dbCtx.DbSetProdItem
.Where(p => p.OrderRowID == orderRowID)
.ExecuteUpdateAsync(setters => setters
.SetProperty(p => p.ProdAssignID, (int?)null)
.SetProperty(p => p.EstimTime, 0)
);
}
catch (Exception exc)
{
Log.Error($"Eccezione durante ProdItemResetOrderAssign{Environment.NewLine}{exc}");
}
}
return numItem;
}
internal async Task<List<ProductionPlantModel>> ProdPlantGetAllAsync()
{
List<ProductionPlantModel> dbResult = new List<ProductionPlantModel>();
@@ -1609,7 +1609,7 @@ namespace EgwCoreLib.Lux.Data.Services
sw.Start();
List<ProductionAssignModel>? result = new List<ProductionAssignModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:ProdAssign:OrdRow:{OrderRowID}";
string currKey = $"{redisBaseKey}:ProdAssign:OrdRowId:{OrderRowID}";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue && rawData.Length() > 2)
{
@@ -1645,7 +1645,7 @@ namespace EgwCoreLib.Lux.Data.Services
sw.Start();
List<ProductionAssignModel>? result = new List<ProductionAssignModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:ProdAssign:OrdRow:{OrderRowID}";
string currKey = $"{redisBaseKey}:ProdAssign:OrdRowId:{OrderRowID}";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue && rawData.Length() > 2)
{
@@ -1680,7 +1680,10 @@ namespace EgwCoreLib.Lux.Data.Services
// calcolo
int fatto = await dbController.ProdItemBulkAssignItems(OrderRowId, ProdAssignId, itemsToAssign);
// svuoto cache...
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ProdItems:*");
//await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ProdItems:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ProdItems:OrdRowId:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ProdAssign:OrdRowId:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OrderRows:*");
sw.Stop();
Log.Debug($"ProdItemBulkAssignItems in {sw.Elapsed.TotalMilliseconds} ms");
return fatto;
@@ -1721,6 +1724,20 @@ namespace EgwCoreLib.Lux.Data.Services
return result;
}
/// <summary>
/// Reset assegnazioni x ProdItems dato OrderRowID
/// </summary>
/// <param name="orderRowID"></param>
/// <returns></returns>
public async Task<int> ProdItemResetOrderAssign(int orderRowID)
{
int result = await dbController.ProdItemResetOrderAssign(orderRowID);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ProdItems:OrdRowId:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ProdAssign:OrdRowId:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OrderRows:*");
return result;
}
/// <summary>
/// Elenco completo plant gestiti
/// </summary>
+1 -1
View File
@@ -4,7 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>0.9.2512.2415</Version>
<Version>0.9.2512.2417</Version>
</PropertyGroup>
<ItemGroup>
+1 -1
View File
@@ -454,7 +454,7 @@ else if (WorkLoadRecord != null)
{
if (CurrEditMode == EditMode.WorkLoadDetailTime)
{
<TimeEstim DetailRecord="WorkLoadRecord" OrderRowRecord="SelRecord" AllProdAss="ListProdAssign" EC_ReRunReq="ReRunJob" EC_ClosePopup="ClosePopup" EC_RunBalance="ReqBalance"></TimeEstim>
<TimeEstim DetailRecord="WorkLoadRecord" OrderRowRecord="SelRecord" AllProdAss="ListProdAssign" EC_ReRunReq="ReRunJob" EC_ClosePopup="ClosePopup" EC_RunBalance="ReqBalance" EC_DirectAssign="DirectAssign" EC_ResetAssign="ResetAssign"></TimeEstim>
}
else if (CurrEditMode == EditMode.WorkLoadDetailTag)
{
@@ -8,6 +8,7 @@ using EgwCoreLib.Lux.Data.DbModel.Utils;
using EgwCoreLib.Lux.Data.Migrations;
using EgwCoreLib.Lux.Data.Services;
using Lux.UI.Components.Compo.WorkLoad;
using Lux.UI.Components.Pages;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Cors.Infrastructure;
@@ -1816,5 +1817,46 @@ namespace Lux.UI.Components.Compo
}
#endregion Private Methods
/// <summary>
/// Esegue assegnazione diretta valori ricevuti
/// </summary>
/// <param name="updInfo"></param>
/// <returns></returns>
private async Task DirectAssign(DirectAssignReqDto updInfo)
{
if (updInfo != null)
{
int numDone= await DLService.ProdItemBulkAssignItems(updInfo.OrderRowID, updInfo.ProdAssignID, updInfo.DictPartAssign);
// fix display
await ForceOrderReload(updInfo.OrderRowID);
}
}
/// <summary>
/// Esegue reset assegnazioni
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private async Task ResetAssign(int OrderRowID)
{
await DLService.ProdItemResetOrderAssign(OrderRowID);
await ForceOrderReload(OrderRowID);
}
private async Task ForceOrderReload(int OrderRowID)
{
// chiudo modale...
ClosePopup();
// rileggo i dati!
await ReloadData();
// recupero il record con orderId indicato
var item = ListRecords.FirstOrDefault(x => x.Original.OrderRowID == OrderRowID);
if (item != null)
{
// seleziono nuovamente i dati...
await ShowWLD(item, EditMode.WorkLoadDetailTime);
}
}
}
}
@@ -26,7 +26,7 @@
<div class="modal-body">
<div class="row">
<div class="col">
@if (setBalance)
@if (CurrMode == SetMode.Balance)
{
<div class="row">
<div class="col-4">
@@ -60,6 +60,74 @@
</div>
</div>
}
@* else if (CurrMode == SetMode.AssignUnwork)
{
<div class="row">
<div class="col-4">
<div class="input-group">
<span class="input-group-text" id="inputGroup-sizing-default">Lungh.Barre</span>
<input type="number" class="form-control text-end" @bind="BarLenght">
<span class="input-group-text" id="inputGroup-sizing-default">mm</span>
</div>
</div>
<div class="col-6">
<div class="input-group">
@if (AllProdAss != null && AllProdAss.Count > 0)
{
foreach (var item in AllProdAss)
{
<span class="input-group-text" id="inputGroup-sizing-default">@item.ProdPlantCod</span>
<input type="number" class="form-control" @bind="@DictPercReq[item.ProdPlantCod]">
}
}
</div>
</div>
<div class="col-2">
@if (checkSend)
{
<button class="btn btn-danger w-100" @onclick="SendUnworkExt">Escludi <i class="fa-solid fa-triangle-exclamation"></i></button>
}
else
{
<button class="btn btn-secondary w-100 disabled">Escludi <i class="fa-solid fa-scale-balanced"></i></button>
}
</div>
</div>
} *@
else if (CurrMode == SetMode.AssignOk)
{
<div class="row">
<div class="col-4">
<div class="input-group">
<span class="input-group-text" id="inputGroup-sizing-default">Lungh.Barre</span>
<input type="number" class="form-control text-end" @bind="BarLenght">
<span class="input-group-text" id="inputGroup-sizing-default">mm</span>
</div>
</div>
<div class="col-6">
<div class="input-group">
@if (AllProdAss != null && AllProdAss.Count > 0)
{
foreach (var item in AllProdAss)
{
<span class="input-group-text" id="inputGroup-sizing-default">@item.ProdPlantCod</span>
<input type="number" class="form-control" @bind="@DictPercReq[item.ProdPlantCod]">
}
}
</div>
</div>
<div class="col-2">
@if (checkSend)
{
<button class="btn btn-info w-100" @onclick="DirectAssign">Assegna <i class="fa-solid fa-plus"></i></button>
}
else
{
<button class="btn btn-secondary w-100 disabled">Assegna <i class="fa-solid fa-thumbtack"></i></button>
}
</div>
</div>
}
</div>
</div>
<div class="row">
@@ -70,7 +138,7 @@
<li class="list-group-item d-flex justify-content-between">
<div class="col-9">Non producibili: <span class="fw-bold px-1">@DetailRecord.NumKo</span></div>
<div class="col-3">
<button class="btn btn-sm btn-danger w-100" @onclick="SendUnworkExt">Send EXT <i class="fa-solid fa-triangle-exclamation"></i></button>
<button class="btn btn-sm btn-danger w-100" @onclick="() => ToggleAssignMode(DetailRecord.ListWorkable, SetMode.AssignUnwork)" title="Assegna per Esclusione / Manuale / Esterno">Escludi <i class="fa-solid fa-triangle-exclamation"></i></button>
</div>
</li>
}
@@ -79,7 +147,7 @@
<li class="list-group-item d-flex justify-content-between">
<div class="col-9">Vincolati: <span class="fw-bold px-1">@DetailRecord.NumOkVin</span></div>
<div class="col-3">
<button class="btn btn-sm btn-info w-100">Assegna <i class="fa-solid fa-thumbtack"></i></button>
<button class="btn btn-sm btn-info w-100" @onclick="() => ToggleAssignMode(DetailRecord.ListWorkable, SetMode.AssignOk)">Assegna <i class="fa-solid fa-thumbtack"></i></button>
</div>
</li>
}
@@ -88,7 +156,7 @@
<li class="list-group-item d-flex justify-content-between">
<div class="col-9">Producibili ovunque: <span class="fw-bold px-1">@DetailRecord.NumOk</span></div>
<div class="col-3">
<button class="btn btn-sm btn-success w-100" @onclick="() => ToggleBalance(DetailRecord.ListWorkable)">Dividi <i class="fa-solid fa-scissors"></i></button>
<button class="btn btn-sm btn-success w-100" @onclick="() => ToggleAssignMode(DetailRecord.ListWorkable, SetMode.Balance)">Bilancia <i class="fa-solid fa-scale-unbalanced"></i></button>
</div>
</li>
}
@@ -99,7 +167,7 @@
<thead>
<tr>
<th>
SEL
<button class="btn btn-sm btn-success" @onclick="() => ToggleAssignMode(DetailRecord.ListWorkable, SetMode.None)"><i class="fa-solid fa-rotate-right"></i></button>
</th>
<th>
Impianto
@@ -119,15 +187,19 @@
{
<tr>
<td>
@if (CurrMode == SetMode.AssignUnwork && item.ProdPlantCod.Contains("EXT"))
{
<button class="btn btn-sm btn-danger" @onclick="() => SendUnworkExt(item.ProdAssignID)" title="Escludi in blocco"><i class="fa-solid fa-arrow-right-from-bracket"></i> <i class="fa-solid fa-triangle-exclamation"></i></button>
}
</td>
<td>
@item.ProdPlantCod
</td>
<td class="text-end">
@item.NumParts / 0%
@item.NumParts / @($"{NumPartRatio(item.NumParts):P1}")
</td>
<td class="text-end">
@item.TotalEstimTime / 0%
@item.TotalEstimTime / @($"{TotMaxTimeRatio(item.TotalEstimTime):P1}")
</td>
</tr>
}
@@ -142,7 +214,7 @@
<tfoot>
<tr>
<td colspan="4">
<button class="btn btn-warning">Reset Assegnazioni</button>
<button class="btn btn-warning" @onclick="ForceResetAssign">Reset Assegnazioni</button>
</td>
</tr>
</tfoot>
@@ -4,6 +4,7 @@ using EgwCoreLib.Lux.Data.DbModel.Production;
using EgwCoreLib.Lux.Data.DbModel.Sales;
using EgwCoreLib.Lux.Data.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using NLog.Targets.Wrappers;
namespace Lux.UI.Components.Compo.WorkLoad
@@ -12,9 +13,6 @@ namespace Lux.UI.Components.Compo.WorkLoad
{
#region Public Properties
[Parameter]
public OrderRowModel OrderRowRecord { get; set; } = null!;
[Parameter]
public List<ProductionAssignModel>? AllProdAss { get; set; }
@@ -29,17 +27,56 @@ namespace Lux.UI.Components.Compo.WorkLoad
[Parameter]
public EventCallback<BalanceReqDto> EC_RunBalance { get; set; }
[Parameter]
public EventCallback<DirectAssignReqDto> EC_DirectAssign { get; set; }
[Parameter]
public EventCallback<int> EC_ResetAssign { get; set; }
[Parameter]
public OrderRowModel OrderRowRecord { get; set; } = null!;
#endregion Public Properties
#region Protected Fields
[Inject]
protected DataLayerServices DLService { get; set; } = null!;
protected BalanceReqDto cBalanceReq = new BalanceReqDto();
/// <summary>
/// Gestione modalità assegnazione Parts a macchine
/// </summary>
protected SetMode CurrMode = SetMode.None;
#endregion Protected Fields
#region Protected Enums
protected enum SetMode
{
/// <summary>
/// Modalità non definita
/// </summary>
None = 0,
/// <summary>
/// Modalità preparazione richiesta balance
/// </summary>
Balance,
/// <summary>
/// Assegnazione diretta pezzi OK
/// </summary>
AssignOk,
/// <summary>
/// Assegnazione pezzi non lavorabili
/// </summary>
AssignUnwork
}
#endregion Protected Enums
#region Protected Properties
protected int BarLenght
@@ -59,6 +96,8 @@ namespace Lux.UI.Components.Compo.WorkLoad
set => cBalanceReq.MachineBalance = value;
}
[Inject]
protected DataLayerServices DLService { get; set; } = null!;
protected string MainCss
{
@@ -73,10 +112,40 @@ namespace Lux.UI.Components.Compo.WorkLoad
{
await EC_ClosePopup.InvokeAsync(true);
}
protected override void OnParametersSet()
protected async Task ForceResetAssign()
{
ReloadData();
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di vole resettare tutte le assegnazioniper gli articoli?"))
return;
await EC_ResetAssign.InvokeAsync(OrderRowRecord.OrderRowID);
}
protected async Task DirectAssign()
{
await Task.Delay(200);
}
protected override async Task OnParametersSetAsync()
{
await ReloadData();
}
private double TotMaxTimeRatio(double currTime)
{
return DetailRecord.TotMaxTime > 0 ? currTime / (double)DetailRecord.TotMaxTime : 0.0;
}
private double NumPartRatio(int numPart)
{
return DetailRecord.NumParts > 0 ? (double)numPart / DetailRecord.NumParts : 0.0;
}
/// <summary>
/// Rilettura dati da DB associazioni
/// </summary>
/// <returns></returns>
protected async Task ReloadData()
{
await Task.Delay(500);
}
protected async Task ReqBalance()
@@ -89,46 +158,47 @@ namespace Lux.UI.Components.Compo.WorkLoad
await EC_ReRunReq.InvokeAsync(true);
}
protected void ToggleBalance(List<string> listTag)
{
setBalance = !setBalance;
if (setBalance)
{
cBalanceReq.TagsList = listTag;
ResetDictReq();
}
}
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
/// <summary>
/// Assegnazione item unworkable a EXT
/// </summary>
/// <returns></returns>
protected async Task SendUnworkExt()
protected async Task SendUnworkExt(int ProdAssignId)
{
// recupero ProdAssignId x EXT... da elenco record mostrati...
int ProdAssignId = 0;
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi assegnazione Ext/Manual per {DetailRecord.NumKo} articoli?"))
return;
if (OrderRowRecord.OrderRowID > 0 && ProdAssignId > 0)
{
// recupero elenco e converto in Dict
var dictUnwokr = DetailRecord
var dictPartUnwokr = DetailRecord
.ListUnWorkable
.ToDictionary(x => x, x => 0.0);
// chiamo update!
await DLService.ProdItemBulkAssignItems(OrderRowRecord.OrderRowID, ProdAssignId, dictUnwokr);
DirectAssignReqDto assignReq = new DirectAssignReqDto()
{
OrderRowID = OrderRowRecord.OrderRowID,
ProdAssignID = ProdAssignId,
DictPartAssign = dictPartUnwokr
};
await EC_DirectAssign.InvokeAsync(assignReq);
CurrMode = SetMode.None;
}
// rileggo!
await ReloadData();
}
/// <summary>
/// Rilettura dati da DB associazioni
/// </summary>
/// <returns></returns>
protected async Task ReloadData()
protected void ToggleAssignMode(List<string> listTag, SetMode reqMode)
{
await Task.Delay(500);
// se era già attivo --> chiude... altrimenti assegna!
CurrMode = CurrMode == reqMode ? SetMode.None : reqMode;
// ora imposto secondo modo...
if (CurrMode == reqMode)
{
cBalanceReq.TagsList = listTag;
PrepareSendReq();
}
}
#endregion Protected Methods
@@ -137,13 +207,11 @@ namespace Lux.UI.Components.Compo.WorkLoad
private string selUid = "";
private bool setBalance = false;
#endregion Private Fields
#region Private Methods
private void ResetDictReq()
private void PrepareSendReq()
{
DictPercReq.Clear();
var cList = AllProdAss;
@@ -152,7 +220,31 @@ namespace Lux.UI.Components.Compo.WorkLoad
{
stdPerc = 1.0 / (cList.Count - 1);
}
DictPercReq = cList.ToDictionary(x => x.ProdPlantCod, x => x.ProdPlantCod != "EXT" ? stdPerc : 0);
if (cList != null)
{
// valutare se determinare EXT/manuale in modo diverso da ProdPlantModel
switch (CurrMode)
{
case SetMode.None:
break;
case SetMode.Balance:
DictPercReq = cList.ToDictionary(x => x.ProdPlantCod, x => x.ProdPlantCod != "EXT" ? stdPerc : 0.0);
break;
case SetMode.AssignOk:
DictPercReq = cList.ToDictionary(x => x.ProdPlantCod, x => 0.0);
break;
case SetMode.AssignUnwork:
DictPercReq = cList.ToDictionary(x => x.ProdPlantCod, x => x.ProdPlantCod == "EXT" ? 1.0 : 0.0);
break;
default:
break;
}
}
}
#endregion Private Methods
+1 -1
View File
@@ -5,7 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>aspnet-Lux.UI-a758c101-a2f4-4e38-977d-1c4887dbbd50</UserSecretsId>
<Version>0.9.2512.2415</Version>
<Version>0.9.2512.2417</Version>
</PropertyGroup>
<ItemGroup>
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>LUX - Web Windows MES</i>
<h4>Versione: 0.9.2512.2415</h4>
<h4>Versione: 0.9.2512.2417</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
0.9.2512.2415
0.9.2512.2417
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>0.9.2512.2415</version>
<version>0.9.2512.2417</version>
<url>http://nexus.steamware.net/repository/SWS/GPW/stable/GPW.UI.zip</url>
<changelog>http://nexus.steamware.net/repository/SWS/GPW/stable/ChangeLog.html</changelog>
<mandatory>false</mandatory>