328 lines
12 KiB
C#
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
|
|
}
|
|
} |