style: apply CSharpier formatting to all C# sources
Repo-wide sweep with the pinned CSharpier 1.3.0 tool. Whitespace and line-wrapping only; OpenNest.Engine.Tests (109) and OpenNest.IO.Tests pass after reformat, full solution builds 0 errors. Added .csharpierignore so csproj/config XML keeps its existing layout (CSharpier's XML wrapping churns attributes with zero benefit). Formatting is now enforceable: dotnet csharpier check . passes.
This commit is contained in:
@@ -6,17 +6,20 @@ namespace OpenNest.Api;
|
||||
public class NestRequest
|
||||
{
|
||||
public IReadOnlyList<NestRequestPart> Parts { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Explicit available physical stock. Null keeps the legacy unlimited SheetSize fallback;
|
||||
/// an empty list deliberately means no stock is available.
|
||||
/// </summary>
|
||||
public IReadOnlyList<NestRequestPlate> Plates { get; init; }
|
||||
public Size SheetSize { get; init; } = new(60, 120);
|
||||
|
||||
/// <summary>Built-in whole-job placement strategy. Explicit values take precedence over legacy Strategy.</summary>
|
||||
public string PlacementStrategy { get; init; } = "Default";
|
||||
public string Material { get; init; } = "Steel, A1011 HR";
|
||||
public double Thickness { get; init; } = 0.06;
|
||||
public double Spacing { get; init; } = 0.1;
|
||||
|
||||
/// <summary>Legacy compatibility setting; Auto maps to the Default whole-job strategy.</summary>
|
||||
public NestStrategy Strategy { get; init; } = NestStrategy.Auto;
|
||||
public CutParameters Cutting { get; init; } = CutParameters.Default;
|
||||
|
||||
@@ -7,6 +7,7 @@ public class NestRequestPlate
|
||||
{
|
||||
public string Id { get; init; }
|
||||
public Size Size { get; init; }
|
||||
|
||||
/// <summary>Available physical sheets; null means unlimited.</summary>
|
||||
public int? Quantity { get; init; }
|
||||
public double PartSpacing { get; init; }
|
||||
|
||||
@@ -25,10 +25,12 @@ public class NestResponse
|
||||
/// <summary>Zero identifies an archive written before response metadata was versioned.</summary>
|
||||
public int SchemaVersion { get; init; } = CurrentSchemaVersion;
|
||||
public int SheetCount { get; init; }
|
||||
|
||||
/// <summary>Placed-part area divided by total materialized physical-sheet area, as a 0.0–1.0 ratio.</summary>
|
||||
public double Utilization { get; init; }
|
||||
public TimeSpan CutTime { get; init; }
|
||||
public TimeSpan Elapsed { get; init; }
|
||||
|
||||
/// <summary>Null means an older archive did not record whole-job fulfillment status.</summary>
|
||||
public NestJobStatus? Status { get; init; }
|
||||
public NestJobStopReason? StopReason { get; init; }
|
||||
@@ -43,7 +45,7 @@ public class NestResponse
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
WriteIndented = true,
|
||||
IncludeFields = true, // Required for OpenNest.Geometry.Size and Spacing public fields.
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
Converters = { new JsonStringEnumConverter() },
|
||||
};
|
||||
|
||||
public async Task SaveAsync(string path)
|
||||
@@ -61,19 +63,27 @@ public class NestResponse
|
||||
var responseEntry = zip.CreateEntry("response.json");
|
||||
await using (var stream = responseEntry.Open())
|
||||
{
|
||||
await JsonSerializer.SerializeAsync(stream, new NestResponseArchiveDto
|
||||
{
|
||||
SchemaVersion = CurrentSchemaVersion,
|
||||
SheetCount = SheetCount,
|
||||
Utilization = Utilization,
|
||||
CutTimeTicks = CutTime.Ticks,
|
||||
ElapsedTicks = Elapsed.Ticks,
|
||||
Status = Status,
|
||||
StopReason = StopReason,
|
||||
Fulfillment = Fulfillment is null ? [] : new List<NestPartFulfillment>(Fulfillment),
|
||||
StockUsage = StockUsage is null ? [] : new List<NestStockUsage>(StockUsage),
|
||||
PlateStockMappings = PlateStockMappings is null ? [] : new List<NestPlateStockMapping>(PlateStockMappings)
|
||||
}, JsonOptions);
|
||||
await JsonSerializer.SerializeAsync(
|
||||
stream,
|
||||
new NestResponseArchiveDto
|
||||
{
|
||||
SchemaVersion = CurrentSchemaVersion,
|
||||
SheetCount = SheetCount,
|
||||
Utilization = Utilization,
|
||||
CutTimeTicks = CutTime.Ticks,
|
||||
ElapsedTicks = Elapsed.Ticks,
|
||||
Status = Status,
|
||||
StopReason = StopReason,
|
||||
Fulfillment = Fulfillment is null
|
||||
? []
|
||||
: new List<NestPartFulfillment>(Fulfillment),
|
||||
StockUsage = StockUsage is null ? [] : new List<NestStockUsage>(StockUsage),
|
||||
PlateStockMappings = PlateStockMappings is null
|
||||
? []
|
||||
: new List<NestPlateStockMapping>(PlateStockMappings),
|
||||
},
|
||||
JsonOptions
|
||||
);
|
||||
}
|
||||
|
||||
var nestEntry = zip.CreateEntry("nest.nest");
|
||||
@@ -91,16 +101,19 @@ public class NestResponse
|
||||
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
using var zip = new ZipArchive(fs, ZipArchiveMode.Read);
|
||||
|
||||
var requestEntry = zip.GetEntry("request.json")
|
||||
var requestEntry =
|
||||
zip.GetEntry("request.json")
|
||||
?? throw new InvalidOperationException("Missing request.json in .nestquote file");
|
||||
NestRequest request;
|
||||
await using (var stream = requestEntry.Open())
|
||||
{
|
||||
request = await JsonSerializer.DeserializeAsync<NestRequest>(stream, JsonOptions)
|
||||
request =
|
||||
await JsonSerializer.DeserializeAsync<NestRequest>(stream, JsonOptions)
|
||||
?? throw new InvalidOperationException("Invalid request.json in .nestquote file");
|
||||
}
|
||||
|
||||
var responseEntry = zip.GetEntry("response.json")
|
||||
var responseEntry =
|
||||
zip.GetEntry("response.json")
|
||||
?? throw new InvalidOperationException("Missing response.json in .nestquote file");
|
||||
NestResponseArchiveDto archive;
|
||||
var hasSchemaVersion = false;
|
||||
@@ -110,16 +123,19 @@ public class NestResponse
|
||||
{
|
||||
var root = document.RootElement;
|
||||
hasSchemaVersion = root.TryGetProperty("schemaVersion", out _);
|
||||
hasStatusMetadata = root.TryGetProperty("status", out _) ||
|
||||
root.TryGetProperty("stopReason", out _) ||
|
||||
root.TryGetProperty("fulfillment", out _) ||
|
||||
root.TryGetProperty("stockUsage", out _) ||
|
||||
root.TryGetProperty("plateStockMappings", out _);
|
||||
archive = root.Deserialize<NestResponseArchiveDto>(JsonOptions)
|
||||
hasStatusMetadata =
|
||||
root.TryGetProperty("status", out _)
|
||||
|| root.TryGetProperty("stopReason", out _)
|
||||
|| root.TryGetProperty("fulfillment", out _)
|
||||
|| root.TryGetProperty("stockUsage", out _)
|
||||
|| root.TryGetProperty("plateStockMappings", out _);
|
||||
archive =
|
||||
root.Deserialize<NestResponseArchiveDto>(JsonOptions)
|
||||
?? throw new InvalidOperationException("Invalid response.json in .nestquote file");
|
||||
}
|
||||
|
||||
var nestEntry = zip.GetEntry("nest.nest")
|
||||
var nestEntry =
|
||||
zip.GetEntry("nest.nest")
|
||||
?? throw new InvalidOperationException("Missing nest.nest in .nestquote file");
|
||||
Nest nest;
|
||||
using (var nestMs = new MemoryStream())
|
||||
@@ -145,7 +161,7 @@ public class NestResponse
|
||||
StockUsage = hasStatusMetadata ? archive.StockUsage ?? [] : [],
|
||||
PlateStockMappings = hasStatusMetadata ? archive.PlateStockMappings ?? [] : [],
|
||||
Nest = nest,
|
||||
Request = request
|
||||
Request = request,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+104
-46
@@ -16,10 +16,13 @@ public static class NestRunner
|
||||
public static Task<NestResponse> RunAsync(
|
||||
NestRequest request,
|
||||
IProgress<NestProgress> progress = null,
|
||||
CancellationToken token = default)
|
||||
CancellationToken token = default
|
||||
)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(request);
|
||||
var requestParts = request.Parts ?? throw new ArgumentException("Request parts must not be null.", nameof(request));
|
||||
var requestParts =
|
||||
request.Parts
|
||||
?? throw new ArgumentException("Request parts must not be null.", nameof(request));
|
||||
if (requestParts.Count == 0)
|
||||
throw new ArgumentException("Request must contain at least one part.", nameof(request));
|
||||
|
||||
@@ -32,22 +35,32 @@ public static class NestRunner
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
if (!File.Exists(part.Request.DxfPath))
|
||||
throw new FileNotFoundException($"DXF file not found: {part.Request.DxfPath}", part.Request.DxfPath);
|
||||
throw new FileNotFoundException(
|
||||
$"DXF file not found: {part.Request.DxfPath}",
|
||||
part.Request.DxfPath
|
||||
);
|
||||
|
||||
if (!importedByPath.TryGetValue(part.Request.DxfPath, out var drawing))
|
||||
{
|
||||
try
|
||||
{
|
||||
drawing = CadImporter.ImportDrawing(part.Request.DxfPath,
|
||||
new CadImportOptions { Quantity = part.Request.Quantity });
|
||||
drawing = CadImporter.ImportDrawing(
|
||||
part.Request.DxfPath,
|
||||
new CadImportOptions { Quantity = part.Request.Quantity }
|
||||
);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to import DXF: {part.Request.DxfPath}", exception);
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to import DXF: {part.Request.DxfPath}",
|
||||
exception
|
||||
);
|
||||
}
|
||||
|
||||
if (drawing.Program == null || drawing.Program.Codes.Count == 0)
|
||||
throw new InvalidOperationException($"Failed to import DXF: {part.Request.DxfPath}");
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to import DXF: {part.Request.DxfPath}"
|
||||
);
|
||||
|
||||
importedByPath.Add(part.Request.DxfPath, drawing);
|
||||
}
|
||||
@@ -56,8 +69,11 @@ public static class NestRunner
|
||||
jobParts.Add(DrawingJobMapper.FromDrawing(part.Id, drawing, part.Request.Quantity));
|
||||
}
|
||||
|
||||
var job = new NestJob(jobParts, CreateStock(request),
|
||||
new NestJobOptions(ResolvePlacementStrategy(request)));
|
||||
var job = new NestJob(
|
||||
jobParts,
|
||||
CreateStock(request),
|
||||
new NestJobOptions(ResolvePlacementStrategy(request))
|
||||
);
|
||||
var jobProgress = progress == null ? null : new JobProgressBridge(progress);
|
||||
var result = new NestJobRunner(PlateNesterFactory.Create).Solve(job, jobProgress, token);
|
||||
|
||||
@@ -71,35 +87,56 @@ public static class NestRunner
|
||||
var cutTime = Timing.CalculateTime(timingInfo, request.Cutting);
|
||||
sw.Stop();
|
||||
|
||||
return Task.FromResult(new NestResponse
|
||||
{
|
||||
SheetCount = nest.Plates.Count,
|
||||
Utilization = CalculateUtilization(nest),
|
||||
CutTime = cutTime,
|
||||
Elapsed = sw.Elapsed,
|
||||
Status = result.Status,
|
||||
StopReason = result.StopReason,
|
||||
Fulfillment = result.Fulfillment
|
||||
.Select(value => new NestPartFulfillment(value.PartId, value.Requested, value.Placed, value.Unplaced))
|
||||
.ToArray(),
|
||||
StockUsage = result.StockUsage
|
||||
.Select(value => new NestStockUsage(value.StockId, value.Used, value.Remaining))
|
||||
.ToArray(),
|
||||
PlateStockMappings = result.Plates
|
||||
.Select(value => new NestPlateStockMapping(value.PlateIndex, value.StockId))
|
||||
.ToArray(),
|
||||
Nest = nest,
|
||||
Request = request
|
||||
});
|
||||
return Task.FromResult(
|
||||
new NestResponse
|
||||
{
|
||||
SheetCount = nest.Plates.Count,
|
||||
Utilization = CalculateUtilization(nest),
|
||||
CutTime = cutTime,
|
||||
Elapsed = sw.Elapsed,
|
||||
Status = result.Status,
|
||||
StopReason = result.StopReason,
|
||||
Fulfillment = result
|
||||
.Fulfillment.Select(value => new NestPartFulfillment(
|
||||
value.PartId,
|
||||
value.Requested,
|
||||
value.Placed,
|
||||
value.Unplaced
|
||||
))
|
||||
.ToArray(),
|
||||
StockUsage = result
|
||||
.StockUsage.Select(value => new NestStockUsage(
|
||||
value.StockId,
|
||||
value.Used,
|
||||
value.Remaining
|
||||
))
|
||||
.ToArray(),
|
||||
PlateStockMappings = result
|
||||
.Plates.Select(value => new NestPlateStockMapping(
|
||||
value.PlateIndex,
|
||||
value.StockId
|
||||
))
|
||||
.ToArray(),
|
||||
Nest = nest,
|
||||
Request = request,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<IdentifiedRequestPart> IdentifyParts(IReadOnlyList<NestRequestPart> requestParts)
|
||||
private static IReadOnlyList<IdentifiedRequestPart> IdentifyParts(
|
||||
IReadOnlyList<NestRequestPart> requestParts
|
||||
)
|
||||
{
|
||||
var identified = new List<IdentifiedRequestPart>(requestParts.Count);
|
||||
var ids = new HashSet<string>(StringComparer.Ordinal);
|
||||
for (var index = 0; index < requestParts.Count; index++)
|
||||
{
|
||||
var part = requestParts[index] ?? throw new ArgumentException("Request parts must not contain null entries.", nameof(requestParts));
|
||||
var part =
|
||||
requestParts[index]
|
||||
?? throw new ArgumentException(
|
||||
"Request parts must not contain null entries.",
|
||||
nameof(requestParts)
|
||||
);
|
||||
var id = part.Id ?? $"part-{index}";
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
throw new ArgumentException("Part IDs must not be blank.", nameof(requestParts));
|
||||
@@ -117,8 +154,12 @@ public static class NestRunner
|
||||
{
|
||||
return
|
||||
[
|
||||
new NestPlateStock(LegacyStockId, request.SheetSize, quantity: null,
|
||||
partSpacing: request.Spacing)
|
||||
new NestPlateStock(
|
||||
LegacyStockId,
|
||||
request.SheetSize,
|
||||
quantity: null,
|
||||
partSpacing: request.Spacing
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -126,9 +167,20 @@ public static class NestRunner
|
||||
foreach (var plate in request.Plates)
|
||||
{
|
||||
if (plate is null)
|
||||
throw new ArgumentException("Request plates must not contain null entries.", nameof(request));
|
||||
stock.Add(new NestPlateStock(plate.Id, plate.Size, plate.Quantity, plate.PartSpacing,
|
||||
plate.EdgeSpacing, plate.Quadrant));
|
||||
throw new ArgumentException(
|
||||
"Request plates must not contain null entries.",
|
||||
nameof(request)
|
||||
);
|
||||
stock.Add(
|
||||
new NestPlateStock(
|
||||
plate.Id,
|
||||
plate.Size,
|
||||
plate.Quantity,
|
||||
plate.PartSpacing,
|
||||
plate.EdgeSpacing,
|
||||
plate.Quadrant
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return stock;
|
||||
@@ -147,25 +199,31 @@ public static class NestRunner
|
||||
}
|
||||
}
|
||||
|
||||
private static string ResolvePlacementStrategy(NestRequest request) => request.PlacementStrategy ?? request.Strategy switch
|
||||
{
|
||||
NestStrategy.Auto => "Default",
|
||||
_ => throw new NotSupportedException($"Unknown legacy nesting strategy: {request.Strategy}.")
|
||||
};
|
||||
private static string ResolvePlacementStrategy(NestRequest request) =>
|
||||
request.PlacementStrategy
|
||||
?? request.Strategy switch
|
||||
{
|
||||
NestStrategy.Auto => "Default",
|
||||
_ => throw new NotSupportedException(
|
||||
$"Unknown legacy nesting strategy: {request.Strategy}."
|
||||
),
|
||||
};
|
||||
|
||||
private static double CalculateUtilization(Nest nest)
|
||||
{
|
||||
var sheetArea = nest.Plates.Sum(plate => plate.Area());
|
||||
if (sheetArea == 0) return 0;
|
||||
var placedArea = nest.Plates.Sum(plate => plate.Parts
|
||||
.Where(part => !part.BaseDrawing.IsCutOff)
|
||||
.Sum(part => part.BaseDrawing.Area));
|
||||
if (sheetArea == 0)
|
||||
return 0;
|
||||
var placedArea = nest.Plates.Sum(plate =>
|
||||
plate.Parts.Where(part => !part.BaseDrawing.IsCutOff).Sum(part => part.BaseDrawing.Area)
|
||||
);
|
||||
return placedArea / sheetArea;
|
||||
}
|
||||
|
||||
private sealed record IdentifiedRequestPart(string Id, NestRequestPart Request);
|
||||
|
||||
private sealed class JobProgressBridge(IProgress<NestProgress> progress) : IProgress<NestJobProgress>
|
||||
private sealed class JobProgressBridge(IProgress<NestProgress> progress)
|
||||
: IProgress<NestJobProgress>
|
||||
{
|
||||
public void Report(NestJobProgress value)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
namespace OpenNest.Api;
|
||||
|
||||
public enum NestStrategy { Auto }
|
||||
public enum NestStrategy
|
||||
{
|
||||
Auto,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user