fix(io): bound bend-note regex execution without partial imports
This commit is contained in:
@@ -0,0 +1,191 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using ACadSharp;
|
||||||
|
using ACadSharp.IO;
|
||||||
|
using CSMath;
|
||||||
|
using OpenNest.Api;
|
||||||
|
using OpenNest.Bending;
|
||||||
|
using OpenNest.Engine.Jobs;
|
||||||
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.IO.Bending;
|
||||||
|
using OpenNest.Mcp;
|
||||||
|
using OpenNest.Mcp.Tools;
|
||||||
|
using CadLayer = ACadSharp.Tables.Layer;
|
||||||
|
using CadLine = ACadSharp.Entities.Line;
|
||||||
|
|
||||||
|
namespace OpenNest.FrontEnd.Tests;
|
||||||
|
|
||||||
|
// Console redirection and both registries are process-static. Reuse the existing
|
||||||
|
// nonparallel front-end collection; these tests require a Windows runtime.
|
||||||
|
[Collection("FrontEndRegistry")]
|
||||||
|
public class RegexImportFailureTests : IDisposable
|
||||||
|
{
|
||||||
|
private const string MarkerLayer = "TESTTIMEOUT";
|
||||||
|
private readonly string directory = Path.Combine(Path.GetTempPath(), "opennest-regex-frontends-" + Guid.NewGuid());
|
||||||
|
private readonly MarkerDetector detector = new();
|
||||||
|
|
||||||
|
public RegexImportFailureTests()
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
BendDetectorRegistry.Register(detector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// No unregister API: retained registrations become inert, and active
|
||||||
|
// registrations only throw for the marker document, never ordinary imports.
|
||||||
|
detector.Active = false;
|
||||||
|
Directory.Delete(directory, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(false)]
|
||||||
|
[InlineData(true)]
|
||||||
|
public void Console_TimeoutExitsOneWithoutOutputEvenAfterEarlierImport(bool earlierSuccess)
|
||||||
|
{
|
||||||
|
var bad = WriteDxf("marker.dxf", marker: true);
|
||||||
|
var hash = Hash(bad);
|
||||||
|
var output = Path.Combine(directory, "output.nest");
|
||||||
|
var args = new List<string>();
|
||||||
|
if (earlierSuccess)
|
||||||
|
args.Add(WriteDxf("good.dxf", marker: false));
|
||||||
|
args.AddRange(new[] { bad, "--size", "20x30", "--output", output,
|
||||||
|
"--repair-bends-mm", "2", "--cad-units", "inches" });
|
||||||
|
var originalOut = System.Console.Out;
|
||||||
|
var originalError = System.Console.Error;
|
||||||
|
using var stdout = new StringWriter();
|
||||||
|
using var stderr = new StringWriter();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
System.Console.SetOut(stdout);
|
||||||
|
System.Console.SetError(stderr);
|
||||||
|
Assert.Equal(1, RunConsole(args.ToArray()));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
System.Console.SetOut(originalOut);
|
||||||
|
System.Console.SetError(originalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(1, detector.CallCount);
|
||||||
|
Assert.Contains("Error: failed to import DXF", stderr.ToString());
|
||||||
|
Assert.Contains(bad, stderr.ToString());
|
||||||
|
if (earlierSuccess)
|
||||||
|
Assert.Contains("Imported: good", stdout.ToString());
|
||||||
|
Assert.DoesNotContain("Bend repair", stdout.ToString());
|
||||||
|
Assert.False(File.Exists(output));
|
||||||
|
Assert.Equal(hash, Hash(bad));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(false)]
|
||||||
|
[InlineData(true)]
|
||||||
|
public async Task Api_TimeoutRetainsInnerExceptionAndNeverSolvesOrReturnsResponse(bool earlierSuccess)
|
||||||
|
{
|
||||||
|
var bad = WriteDxf("marker.dxf", marker: true);
|
||||||
|
var hash = Hash(bad);
|
||||||
|
var engine = new CountingEngine();
|
||||||
|
var engineName = "RegexImportFailure-" + Guid.NewGuid();
|
||||||
|
NestingEngineRegistry.Register(engineName, "test", () => engine);
|
||||||
|
var parts = new List<NestRequestPart>();
|
||||||
|
if (earlierSuccess)
|
||||||
|
parts.Add(new NestRequestPart { Id = "good", DxfPath = WriteDxf("good.dxf", marker: false), Quantity = 1 });
|
||||||
|
parts.Add(new NestRequestPart { Id = "bad", DxfPath = bad, Quantity = 1 });
|
||||||
|
var request = new NestRequest
|
||||||
|
{
|
||||||
|
Parts = parts,
|
||||||
|
Plates = [new NestRequestPlate { Id = "stock", Size = new Size(20, 30), Quantity = 1 }],
|
||||||
|
Engine = engineName,
|
||||||
|
};
|
||||||
|
NestResponse? response = null;
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||||
|
response = await NestRunner.RunAsync(request));
|
||||||
|
|
||||||
|
Assert.IsType<RegexMatchTimeoutException>(exception.InnerException);
|
||||||
|
Assert.Same(detector.Timeout, exception.InnerException);
|
||||||
|
Assert.Contains(bad, exception.Message);
|
||||||
|
Assert.Equal(1, detector.CallCount);
|
||||||
|
Assert.Equal(0, engine.CallCount);
|
||||||
|
Assert.Null(response);
|
||||||
|
Assert.Equal(hash, Hash(bad));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Mcp_TimeoutReturnsErrorWithoutAddingDrawingOrChangingEarlierDrawing()
|
||||||
|
{
|
||||||
|
var good = WriteDxf("good.dxf", marker: false);
|
||||||
|
var bad = WriteDxf("marker.dxf", marker: true);
|
||||||
|
var hash = Hash(bad);
|
||||||
|
var session = new NestSession();
|
||||||
|
var tools = new InputTools(session);
|
||||||
|
Assert.StartsWith("Imported drawing", tools.ImportDxf(good));
|
||||||
|
var earlier = Assert.Single(session.Drawings);
|
||||||
|
var earlierProgram = earlier.Program;
|
||||||
|
var count = session.Drawings.Count;
|
||||||
|
|
||||||
|
var result = tools.ImportDxf(bad);
|
||||||
|
|
||||||
|
Assert.StartsWith("Error:", result);
|
||||||
|
Assert.Contains(bad, result);
|
||||||
|
Assert.Equal(count, session.Drawings.Count);
|
||||||
|
Assert.Same(earlier, Assert.Single(session.Drawings));
|
||||||
|
Assert.Same(earlierProgram, earlier.Program);
|
||||||
|
Assert.Equal(1, detector.CallCount);
|
||||||
|
Assert.Equal(hash, Hash(bad));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int RunConsole(params string[] args)
|
||||||
|
{
|
||||||
|
var method = Assembly.Load("OpenNest.Console").GetType("NestConsole")!
|
||||||
|
.GetMethod("Run", BindingFlags.Static | BindingFlags.Public)!;
|
||||||
|
return (int)method.Invoke(null, new object[] { args })!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string WriteDxf(string name, bool marker)
|
||||||
|
{
|
||||||
|
var document = new CadDocument();
|
||||||
|
document.Header.InsUnits = ACadSharp.Types.Units.UnitsType.Inches;
|
||||||
|
document.Entities.Add(new CadLine(new XYZ(0, 0, 0), new XYZ(2, 0, 0))
|
||||||
|
{
|
||||||
|
Layer = new CadLayer(marker ? MarkerLayer : "0"),
|
||||||
|
});
|
||||||
|
document.Entities.Add(new CadLine(new XYZ(2, 0, 0), new XYZ(2, 2, 0)));
|
||||||
|
document.Entities.Add(new CadLine(new XYZ(2, 2, 0), new XYZ(0, 2, 0)));
|
||||||
|
document.Entities.Add(new CadLine(new XYZ(0, 2, 0), new XYZ(0, 0, 0)));
|
||||||
|
var path = Path.Combine(directory, name);
|
||||||
|
DxfWriter.Write(path, document, false);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] Hash(string path) => SHA256.HashData(File.ReadAllBytes(path));
|
||||||
|
|
||||||
|
private sealed class MarkerDetector : IBendDetector
|
||||||
|
{
|
||||||
|
public string Name { get; } = "FrontEndRegexTimeout-" + Guid.NewGuid();
|
||||||
|
public bool Active { get; set; } = true;
|
||||||
|
public int CallCount { get; private set; }
|
||||||
|
public RegexMatchTimeoutException Timeout { get; } = new("marker", "<test>", TimeSpan.FromSeconds(1));
|
||||||
|
|
||||||
|
public List<Bend> DetectBends(CadDocument document)
|
||||||
|
{
|
||||||
|
if (!Active || !document.Entities.Any(e => e.Layer?.Name == MarkerLayer))
|
||||||
|
return new List<Bend>();
|
||||||
|
CallCount++;
|
||||||
|
throw Timeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class CountingEngine : INestingEngine
|
||||||
|
{
|
||||||
|
public int CallCount { get; private set; }
|
||||||
|
|
||||||
|
public NestJobResult Solve(NestJob job, IProgress<NestJobProgress>? progress = null,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
CallCount++;
|
||||||
|
throw new InvalidOperationException("Import failure must abort before solving.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -17,17 +18,26 @@ namespace OpenNest.IO.Bending
|
|||||||
|
|
||||||
private static readonly Regex BendNoteRegex = new Regex(
|
private static readonly Regex BendNoteRegex = new Regex(
|
||||||
@"(?<direction>UP|DOWN|DN)\s+(?<angle>\d+(\.\d+)?)[^A-Z\d]*R\s*(?<radius>\d+(\.\d+)?)",
|
@"(?<direction>UP|DOWN|DN)\s+(?<angle>\d+(\.\d+)?)[^A-Z\d]*R\s*(?<radius>\d+(\.\d+)?)",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase
|
RegexOptions.Compiled | RegexOptions.IgnoreCase,
|
||||||
|
TimeSpan.FromSeconds(1)
|
||||||
);
|
);
|
||||||
|
|
||||||
private static readonly Regex MTextFormatRegex = new Regex(
|
private static readonly Regex MTextFormatRegex = new Regex(
|
||||||
@"\\[fHCTQWASpOoLlKk][^;]*;|\\P|[{}]|%%[dDpPcC]",
|
@"\\[fHCTQWASpOoLlKk][^;]*;|\\P|[{}]|%%[dDpPcC]",
|
||||||
RegexOptions.Compiled
|
RegexOptions.Compiled,
|
||||||
|
TimeSpan.FromSeconds(1)
|
||||||
);
|
);
|
||||||
|
|
||||||
private static readonly Regex UnicodeEscapeRegex = new Regex(
|
private static readonly Regex UnicodeEscapeRegex = new Regex(
|
||||||
@"\\U\+([0-9A-Fa-f]{4})",
|
@"\\U\+([0-9A-Fa-f]{4})",
|
||||||
RegexOptions.Compiled
|
RegexOptions.Compiled,
|
||||||
|
TimeSpan.FromSeconds(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
private static readonly Regex WhitespaceRegex = new Regex(
|
||||||
|
@"\s+",
|
||||||
|
RegexOptions.None,
|
||||||
|
TimeSpan.FromSeconds(1)
|
||||||
);
|
);
|
||||||
|
|
||||||
public List<Bend> DetectBends(CadDocument document)
|
public List<Bend> DetectBends(CadDocument document)
|
||||||
@@ -238,7 +248,7 @@ namespace OpenNest.IO.Bending
|
|||||||
result = MTextFormatRegex.Replace(result, " ");
|
result = MTextFormatRegex.Replace(result, " ");
|
||||||
|
|
||||||
// Collapse multiple spaces
|
// Collapse multiple spaces
|
||||||
return Regex.Replace(result.Trim(), @"\s+", " ");
|
return WhitespaceRegex.Replace(result.Trim(), " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
private MText FindClosestBendNote(ACadSharp.Entities.Line bendLine, List<MText> notes)
|
private MText FindClosestBendNote(ACadSharp.Entities.Line bendLine, List<MText> notes)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using ACadSharp.IO;
|
using ACadSharp.IO;
|
||||||
using OpenNest.Bending;
|
using OpenNest.Bending;
|
||||||
using OpenNest.IO.Bending;
|
using OpenNest.IO.Bending;
|
||||||
@@ -6,6 +8,50 @@ namespace OpenNest.Tests.Bending;
|
|||||||
|
|
||||||
public class SolidWorksBendDetectorTests
|
public class SolidWorksBendDetectorTests
|
||||||
{
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("BendNoteRegex")]
|
||||||
|
[InlineData("MTextFormatRegex")]
|
||||||
|
[InlineData("UnicodeEscapeRegex")]
|
||||||
|
[InlineData("WhitespaceRegex")]
|
||||||
|
public void DetectorRegex_HasExplicitTimeout(string fieldName)
|
||||||
|
{
|
||||||
|
var field = typeof(SolidWorksBendDetector).GetField(fieldName,
|
||||||
|
BindingFlags.Static | BindingFlags.NonPublic);
|
||||||
|
Assert.NotNull(field);
|
||||||
|
var regex = Assert.IsType<Regex>(field.GetValue(null));
|
||||||
|
Assert.Equal(TimeSpan.FromSeconds(1), regex.MatchTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("UP 90 R0.125", BendDirection.Up, 0.125, "UP 90 R0.125")]
|
||||||
|
[InlineData("DOWN 90 R0.313", BendDirection.Down, 0.313, "DOWN 90 R0.313")]
|
||||||
|
[InlineData("DN 90 SHARP", BendDirection.Down, 0.0, "DN 90 SHARP")]
|
||||||
|
[InlineData(@"{\fArial;UP\P90\U+00B0 R0.125}", BendDirection.Up, 0.125, "UP 90° R0.125")]
|
||||||
|
[InlineData("UP\t90%%d R0.125", BendDirection.Up, 0.125, "UP 90° R0.125")]
|
||||||
|
[InlineData(@"UP 90 R0.125 \fUnterminated", BendDirection.Up, 0.125, @"UP 90 R0.125 \fUnterminated")]
|
||||||
|
public void DetectBends_PreservesDirectionFormattingAndRadius(string text,
|
||||||
|
BendDirection direction, double radius, string expectedNote)
|
||||||
|
{
|
||||||
|
var document = new ACadSharp.CadDocument();
|
||||||
|
document.Entities.Add(new ACadSharp.Entities.Line(new CSMath.XYZ(0, 0, 0), new CSMath.XYZ(10, 0, 0))
|
||||||
|
{
|
||||||
|
Layer = new ACadSharp.Tables.Layer("BEND"),
|
||||||
|
LineType = new ACadSharp.Tables.LineType("CENTER"),
|
||||||
|
});
|
||||||
|
document.Entities.Add(new ACadSharp.Entities.MText
|
||||||
|
{
|
||||||
|
Value = text,
|
||||||
|
InsertPoint = new CSMath.XYZ(5, 0, 0),
|
||||||
|
Height = 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
var bend = Assert.Single(new SolidWorksBendDetector().DetectBends(document));
|
||||||
|
Assert.Equal(direction, bend.Direction);
|
||||||
|
Assert.Equal(90.0, bend.Angle);
|
||||||
|
Assert.Equal(radius, bend.Radius);
|
||||||
|
Assert.Equal(expectedNote, bend.NoteText);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SolidWorksDetector_IsRegistered()
|
public void SolidWorksDetector_IsRegistered()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using ACadSharp;
|
||||||
|
using ACadSharp.IO;
|
||||||
|
using CSMath;
|
||||||
|
using OpenNest.Bending;
|
||||||
|
using OpenNest.IO;
|
||||||
|
using OpenNest.IO.Bending;
|
||||||
|
using CadLayer = ACadSharp.Tables.Layer;
|
||||||
|
using CadLine = ACadSharp.Entities.Line;
|
||||||
|
|
||||||
|
namespace OpenNest.Tests.IO;
|
||||||
|
|
||||||
|
[CollectionDefinition("Bend detector timeout registry", DisableParallelization = true)]
|
||||||
|
public class BendDetectorTimeoutRegistryCollection { }
|
||||||
|
|
||||||
|
[Collection("Bend detector timeout registry")]
|
||||||
|
public class CadImporterTimeoutTests : IDisposable
|
||||||
|
{
|
||||||
|
private const string MarkerLayer = "TESTTIMEOUT";
|
||||||
|
private readonly string directory = Path.Combine(Path.GetTempPath(), "opennest-regex-" + Guid.NewGuid());
|
||||||
|
private readonly List<MarkerDetector> registered = new();
|
||||||
|
|
||||||
|
public CadImporterTimeoutTests() => Directory.CreateDirectory(directory);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Register has no unregister API. Disable every double after its test, and
|
||||||
|
// gate calls on the marker layer while active. Never mutate the private registry.
|
||||||
|
foreach (var detector in registered)
|
||||||
|
detector.Active = false;
|
||||||
|
Directory.Delete(directory, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AutoDetect_OnTimeout_DoesNotFallThroughToSuccessfulDetector()
|
||||||
|
{
|
||||||
|
var document = Document(marker: true);
|
||||||
|
// The built-in detector is registered first; this marker is not a bend line.
|
||||||
|
Assert.Empty(new SolidWorksBendDetector().DetectBends(document));
|
||||||
|
var throwing = Register(throws: true);
|
||||||
|
var fallback = Register(throws: false);
|
||||||
|
Assert.True(BendDetectorRegistry.Detectors.ToList().IndexOf(throwing)
|
||||||
|
< BendDetectorRegistry.Detectors.ToList().IndexOf(fallback));
|
||||||
|
|
||||||
|
var exception = Assert.Throws<RegexMatchTimeoutException>(() => BendDetectorRegistry.AutoDetect(document));
|
||||||
|
|
||||||
|
Assert.Same(throwing.Timeout, exception);
|
||||||
|
Assert.Equal(1, throwing.CallCount);
|
||||||
|
Assert.Equal(0, fallback.CallCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(false)]
|
||||||
|
[InlineData(true)]
|
||||||
|
public void Import_OnTimeout_PublishesNoResultOrRepairReportsAndPreservesSource(bool repair)
|
||||||
|
{
|
||||||
|
var path = WriteDxf("marker.dxf", marker: true);
|
||||||
|
var originalHash = Hash(path);
|
||||||
|
var detector = Register(throws: true);
|
||||||
|
var options = new CadImportOptions
|
||||||
|
{
|
||||||
|
BendDetectorName = detector.Name,
|
||||||
|
BendRepair = repair ? RepairOptions() : null,
|
||||||
|
};
|
||||||
|
var published = new List<CadImportResult>();
|
||||||
|
var reports = new List<BendRepairReport>();
|
||||||
|
|
||||||
|
var exception = Assert.Throws<RegexMatchTimeoutException>(() =>
|
||||||
|
{
|
||||||
|
var result = CadImporter.Import(path, options);
|
||||||
|
// These publication steps must be unreachable, even for an empty result.
|
||||||
|
published.Add(result);
|
||||||
|
reports.AddRange(result.BendRepairReports);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.Same(detector.Timeout, exception);
|
||||||
|
Assert.Equal(1, detector.CallCount);
|
||||||
|
Assert.Empty(published);
|
||||||
|
Assert.Empty(reports);
|
||||||
|
Assert.Equal(originalHash, Hash(path));
|
||||||
|
// Detection precedes both repair and etch conversion in Import. No result
|
||||||
|
// or output escapes; do not inspect its private, disposable scratch geometry.
|
||||||
|
Assert.Equal(new[] { path }, Directory.GetFiles(directory));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ImportDrawing_AfterEarlierSuccess_AbortsBatchBeforePublication()
|
||||||
|
{
|
||||||
|
var good = WriteDxf("good.dxf", marker: false);
|
||||||
|
var bad = WriteDxf("marker.dxf", marker: true);
|
||||||
|
var hashes = new[] { Hash(good), Hash(bad) };
|
||||||
|
var detector = Register(throws: true);
|
||||||
|
var staged = new List<Drawing>();
|
||||||
|
var published = new List<Drawing>();
|
||||||
|
|
||||||
|
var exception = Assert.Throws<RegexMatchTimeoutException>(() =>
|
||||||
|
{
|
||||||
|
foreach (var path in new[] { good, bad })
|
||||||
|
staged.Add(CadImporter.ImportDrawing(path, new CadImportOptions
|
||||||
|
{
|
||||||
|
BendDetectorName = detector.Name,
|
||||||
|
BendRepair = RepairOptions(),
|
||||||
|
}));
|
||||||
|
published.AddRange(staged);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.Same(detector.Timeout, exception);
|
||||||
|
var earlier = Assert.Single(staged);
|
||||||
|
Assert.Equal(good, earlier.Source.Path);
|
||||||
|
Assert.NotEmpty(earlier.Program.Codes);
|
||||||
|
// This first drawing is a discardable local, not a published batch result.
|
||||||
|
Assert.Empty(published);
|
||||||
|
Assert.Equal(1, detector.CallCount);
|
||||||
|
Assert.Equal(hashes, new[] { Hash(good), Hash(bad) });
|
||||||
|
Assert.Equal(new[] { good, bad }.Order(), Directory.GetFiles(directory).Order());
|
||||||
|
}
|
||||||
|
|
||||||
|
private MarkerDetector Register(bool throws)
|
||||||
|
{
|
||||||
|
var detector = new MarkerDetector(throws);
|
||||||
|
BendDetectorRegistry.Register(detector);
|
||||||
|
registered.Add(detector);
|
||||||
|
return detector;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BendRepairOptions RepairOptions() => new()
|
||||||
|
{
|
||||||
|
DrawingUnits = BendRepairUnits.Inches,
|
||||||
|
MaxEndpointMovementMillimeters = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
private string WriteDxf(string name, bool marker)
|
||||||
|
{
|
||||||
|
var path = Path.Combine(directory, name);
|
||||||
|
DxfWriter.Write(path, Document(marker), false);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CadDocument Document(bool marker)
|
||||||
|
{
|
||||||
|
var document = new CadDocument();
|
||||||
|
document.Header.InsUnits = ACadSharp.Types.Units.UnitsType.Inches;
|
||||||
|
document.Entities.Add(new CadLine(new XYZ(0, 0, 0), new XYZ(2, 0, 0))
|
||||||
|
{
|
||||||
|
Layer = new CadLayer(marker ? MarkerLayer : "0"),
|
||||||
|
});
|
||||||
|
document.Entities.Add(new CadLine(new XYZ(2, 0, 0), new XYZ(2, 2, 0)));
|
||||||
|
document.Entities.Add(new CadLine(new XYZ(2, 2, 0), new XYZ(0, 2, 0)));
|
||||||
|
document.Entities.Add(new CadLine(new XYZ(0, 2, 0), new XYZ(0, 0, 0)));
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] Hash(string path) => SHA256.HashData(File.ReadAllBytes(path));
|
||||||
|
|
||||||
|
private sealed class MarkerDetector(bool throws) : IBendDetector
|
||||||
|
{
|
||||||
|
public string Name { get; } = "RegexTimeout-" + Guid.NewGuid();
|
||||||
|
public bool Active { get; set; } = true;
|
||||||
|
public int CallCount { get; private set; }
|
||||||
|
public RegexMatchTimeoutException Timeout { get; } = new("marker", "<test>", TimeSpan.FromSeconds(1));
|
||||||
|
|
||||||
|
public List<Bend> DetectBends(CadDocument document)
|
||||||
|
{
|
||||||
|
if (!Active || !document.Entities.Any(e => e.Layer?.Name == MarkerLayer))
|
||||||
|
return new List<Bend>();
|
||||||
|
CallCount++;
|
||||||
|
if (throws)
|
||||||
|
throw Timeout;
|
||||||
|
return new List<Bend> { new() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user