Files

328 lines
12 KiB
C#

using EgwCoreLib.Lux.Core.RestPayload;
namespace EgwCoreLib.Lux.Core.MachineCalc
{
public class Utils
{
#region Public Methods
/// <summary>
/// Calcolo intersezione delle sole combinazioni calcolabili
/// </summary>
/// <param name="machineResult"></param>
/// <returns></returns>
public static List<MachineTagDTO> CalculateIntersections(List<MachineCalcResultDTO> machineResult)
{
var results = new List<MachineTagDTO>();
if (machineResult == null || !machineResult.Any())
return results;
int numParts = machineResult.FirstOrDefault()?.NumParts ?? 0;
// 1. Mappiamo ogni Tag alla lista di macchine che possono lavorarlo
// Key: Tag (string), Value: Insieme di nomi macchine (HashSet per comparazione veloce)
var tagToMachines = new Dictionary<string, HashSet<string>>();
foreach (var machine in machineResult)
{
var machinableTags = machine.PartList
.Where(p => p.CalcResult == Enums.PartVerificationResult.MACHINABLE);
foreach (var part in machinableTags)
{
if (!tagToMachines.ContainsKey(part.Tag))
tagToMachines[part.Tag] = new HashSet<string>();
tagToMachines[part.Tag].Add(machine.Name);
}
}
// 2. Raggruppiamo i Tag che condividono lo STESSO identico set di macchine
// Usiamo una stringa joinata come chiave per il raggruppamento (es: "MachineA|MachineB")
var groups = tagToMachines.GroupBy(
kvp => string.Join("|", kvp.Value.OrderBy(m => m)), // Chiave univoca per la combinazione di macchine
kvp => kvp.Key // Il Tag
);
// 3. Creiamo i MachineTagDTO per ogni gruppo trovato
foreach (var group in groups)
{
var tagsInGroup = group.ToList();
var machineList = group.Key.Split('|').ToList();
// Calcoliamo i tempi (Min/Max) per questo specifico set di macchine
var timesPerMachine = machineList.Select(mName =>
{
var machineData = machineResult.First(m => m.Name == mName);
return machineData.PartList
.Where(p => tagsInGroup.Contains(p.Tag))
.Sum(p => p.Time);
}).ToList();
results.Add(new MachineTagDTO
{
TotParts = numParts,
Machines = machineList,
Tags = tagsInGroup,
MinTime = timesPerMachine.Any() ? timesPerMachine.Min() : 0,
MaxTime = timesPerMachine.Any() ? timesPerMachine.Max() : 0
});
}
return results;
}
/// <summary>
/// Calcolo intersezioni anche dell'insieme nullo (= UNWORKABLE)
/// </summary>
/// <param name="machineResult"></param>
/// <returns></returns>
public static List<MachineTagDTO> CalculateIntersectionsWithEmpty(List<MachineCalcResultDTO> machineResult)
{
var results = new List<MachineTagDTO>();
if (machineResult == null || !machineResult.Any()) return results;
int numParts = machineResult.First().NumParts;
// 1. Prendiamo l'elenco completo di TUTTI i tag esistenti
var allTags = machineResult.SelectMany(m => m.PartList).Select(p => p.Tag).Distinct();
var tagToMachines = allTags.ToDictionary(tag => tag, _ => new HashSet<string>());
// 2. Popoliamo le macchine solo per i pezzi MACHINABLE
foreach (var machine in machineResult)
{
var machinableTags = machine.PartList
.Where(p => p.CalcResult == Enums.PartVerificationResult.MACHINABLE);
foreach (var part in machinableTags)
{
tagToMachines[part.Tag].Add(machine.Name);
}
}
// 3. Raggruppiamo
var groups = tagToMachines.GroupBy(
kvp => string.Join("|", kvp.Value.OrderBy(m => m)),
kvp => kvp.Key
);
foreach (var group in groups)
{
var tagsInGroup = group.ToList();
// Se la chiave è vuota, significa che il set di macchine è vuoto
var machineList = string.IsNullOrEmpty(group.Key)
? new List<string>()
: group.Key.Split('|').ToList();
var timesPerMachine = machineList.Select(mName =>
{
return machineResult.First(m => m.Name == mName).PartList
.Where(p => tagsInGroup.Contains(p.Tag))
.Sum(p => p.Time);
}).ToList();
results.Add(new MachineTagDTO
{
TotParts = numParts,
Machines = machineList,
Tags = tagsInGroup,
MinTime = timesPerMachine.Any() ? timesPerMachine.Min() : 0,
MaxTime = timesPerMachine.Any() ? timesPerMachine.Max() : 0
});
}
return results;
}
#if false
/// <summary>
/// Calcolo delle intersezioni Macchine/Tags(Parts)
/// </summary>
/// <param name="machineResult"></param>
/// <returns></returns>
public static List<MachineTagDTO> CalculateIntersections(List<MachineCalcResultDTO> machineResult)
{
var results = new List<MachineTagDTO>();
if (machineResult != null && machineResult.Count > 0)
{
int numParts = machineResult.FirstOrDefault()?.NumParts ?? 0;
// Step 1: extract workable tags per machine
var machineTags = machineResult
.ToDictionary(
m => m.Name,
m => m.PartList
.Where(p => p.CalcResult == Enums.PartVerificationResult.MACHINABLE)
.ToList()
);
var machineNames = machineTags.Keys.ToList();
// Step 2: generate all combinations of machines
for (int size = 1; size <= machineNames.Count; size++)
{
foreach (var combo in GetCombinations(machineNames, size))
{
var comboList = combo.ToList();
// Intersection of tags across all machines in this combo
var intersectionTags = comboList
.Select(name => machineTags[name].Select(p => p.Tag).ToHashSet())
.Aggregate((set1, set2) => { set1.IntersectWith(set2); return set1; });
// Compute per-machine sums of Time for these tags
var sums = comboList.Select(name =>
machineTags[name]
.Where(p => intersectionTags.Contains(p.Tag))
.Sum(p => p.Time)
).ToList();
results.Add(new MachineTagDTO
{
TotParts = numParts,
Machines = comboList,
Tags = intersectionTags.ToList(),
MinTime = sums.Any() ? sums.Min() : 0,
MaxTime = sums.Any() ? sums.Max() : 0
});
}
}
// Step 3: add unique (non-intersecting) tags per machine
foreach (var name in machineNames)
{
var uniqueTags = machineTags[name]
.Select(p => p.Tag)
.Except(machineNames.Where(n => n != name)
.SelectMany(n => machineTags[n].Select(p => p.Tag)))
.ToList();
if (uniqueTags.Count > 0)
{
var sum = machineTags[name]
.Where(p => uniqueTags.Contains(p.Tag))
.Sum(p => p.Time);
results.Add(new MachineTagDTO
{
TotParts = numParts,
Machines = new List<string> { name },
Tags = uniqueTags,
MinTime = sum,
MaxTime = sum
});
}
}
}
return results;
}
#endif
#if false
/// <summary>
/// Helper generazione combinazioni di items per una data length (Distinct Combinations)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="length"></param>
/// <returns></returns>
public static IEnumerable<IEnumerable<T>> GetCombinations<T>(IEnumerable<T> items, int length)
{
// Convert to a List to easily access elements by index.
List<T> itemList = items.ToList();
int n = itemList.Count;
// Base case: length 0 returns a collection containing an empty collection
if (length == 0)
{
yield return Enumerable.Empty<T>();
yield break;
}
// Check if enough items are available
if (length > n)
{
yield break;
}
// Call the internal recursive helper function
foreach (var combination in GetCombinationsInternal(itemList, length, 0, n))
{
yield return combination;
}
}
/// <summary>
/// Helper generazione permutazioni di items per una data length
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="length"></param>
/// <returns></returns>
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> items, int length)
{
if (length == 1)
return items.Select(i => new T[] { i });
return GetCombinations(items, length - 1)
.SelectMany(c => items.Where(i => !c.Contains(i)),
(c, i) => c.Concat(new T[] { i }));
}
#endif
/// <summary>
/// Helper calcolo Intersezione lista macchine
/// </summary>
/// <param name="machines"></param>
/// <param name="predicate"></param>
/// <returns></returns>
public static IEnumerable<string> IntersectTags(List<MachineCalcResultDTO> machines, Func<PartCalcDTO, bool> predicate)
{
return machines
.Select(m => m.PartList.Where(predicate).Select(p => p.Tag).ToHashSet())
.Aggregate((set1, set2) => { set1.IntersectWith(set2); return set1; });
}
#endregion Public Methods
#region Private Methods
#if false
private static IEnumerable<IEnumerable<T>> GetCombinationsInternal<T>(
List<T> items,
int length,
int startIndex,
int n)
{
// Base case: combination of length 1
if (length == 1)
{
// Select one item from startIndex up to the end
for (int i = startIndex; i < n; i++)
{
yield return new T[] { items[i] };
}
yield break;
}
// Recursive step
// Iterate over elements starting from startIndex
for (int i = startIndex; i <= n - length; i++)
{
T currentItem = items[i];
// Recursively find combinations of length - 1 starting from the next index (i + 1)
// This is the crucial step that prevents (a,b) and (b,a)
foreach (var subCombination in GetCombinationsInternal(items, length - 1, i + 1, n))
{
// Prepend the current item to the shorter combinations
yield return new T[] { currentItem }.Concat(subCombination);
}
}
}
#endif
#endregion Private Methods
}
}