Files
lux/EgwCoreLib.Lux.Core/MachineCalc/Utils.cs
T
2025-12-09 15:51:33 +01:00

196 lines
7.4 KiB
C#

using EgwCoreLib.Lux.Core.RestPayload;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Core.MachineCalc
{
public class Utils
{
#region Public Methods
/// <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;
}
/// <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 }));
}
/// <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
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);
}
}
}
#endregion Private Methods
}
}