feat: improve DXF export and cross-platform file handling
- PepSettings: add DrawingsDirectory property for DXF endpoint config - DrawingDxfExporter: support subprogram calls, skip non-cut linear moves, remove spurious radians-to-degrees conversion on arc angles, set AC1018 DXF version - DrawingReader: handle .loop-info/.cadsnapshot entries gracefully, derive DrawingInfo from loop names when .info entry is absent, use FileShare.Read - DrawingInfoReader: use TryParse instead of Parse for material number - Drawing: make GetLoopName public (used by DrawingDxfExporter) - ZipHelper: use FileShare.Read to allow concurrent readers on Windows/Linux - NestFilterData: skip status filter when value is "All" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,4 +4,5 @@ public class PepSettings
|
|||||||
{
|
{
|
||||||
public string NestDirectory { get; set; } = string.Empty;
|
public string NestDirectory { get; set; } = string.Empty;
|
||||||
public string MaterialsFile { get; set; } = string.Empty;
|
public string MaterialsFile { get; set; } = string.Empty;
|
||||||
|
public string DrawingsDirectory { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ namespace PepApi.Core.Models
|
|||||||
nests = nests.Where(n => n.DateCreated <= this.EndDate);
|
nests = nests.Where(n => n.DateCreated <= this.EndDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.Status != null)
|
if (this.Status != null && !this.Status.Equals("All", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var x = this.Status.ToUpper();
|
var x = this.Status.ToUpper();
|
||||||
nests = nests.Where(n => n.Status.ToUpper() == x);
|
nests = nests.Where(n => n.Status.ToUpper() == x);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using ACadSharp;
|
using ACadSharp;
|
||||||
using ACadSharp.IO;
|
using ACadSharp.IO;
|
||||||
using ACadSharp.Tables;
|
using ACadSharp.Tables;
|
||||||
using CSMath;
|
using CSMath;
|
||||||
@@ -17,13 +17,43 @@ public static class DrawingDxfExporter
|
|||||||
public static Stream Export(Drawing drawing)
|
public static Stream Export(Drawing drawing)
|
||||||
{
|
{
|
||||||
var doc = new CadDocument();
|
var doc = new CadDocument();
|
||||||
|
doc.Header.Version = ACadVersion.AC1018;
|
||||||
|
|
||||||
|
// Collect loop IDs that are called as subprograms so we skip drawing them as top-level
|
||||||
|
var subprogramIds = new HashSet<int>();
|
||||||
|
foreach (var loop in drawing.Loops)
|
||||||
|
{
|
||||||
|
foreach (var code in loop)
|
||||||
|
{
|
||||||
|
if (code.CodeType() == CodeType.SubProgramCall)
|
||||||
|
subprogramIds.Add(((SubProgramCall)code).LoopId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var loop in drawing.Loops)
|
foreach (var loop in drawing.Loops)
|
||||||
{
|
{
|
||||||
|
var loopNumber = GetLoopNumber(loop.Name);
|
||||||
|
if (subprogramIds.Contains(loopNumber))
|
||||||
|
continue; // drawn inline at SubProgramCall positions
|
||||||
|
|
||||||
var layer = new Layer(loop.Name);
|
var layer = new Layer(loop.Name);
|
||||||
doc.Layers.Add(layer);
|
doc.Layers.Add(layer);
|
||||||
|
|
||||||
var pos = new Vector();
|
DrawLoop(doc, drawing, loop, layer, new Vector());
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = new MemoryStream();
|
||||||
|
using (var writer = new DxfWriter(buffer, doc, false))
|
||||||
|
{
|
||||||
|
writer.Write();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MemoryStream(buffer.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector DrawLoop(CadDocument doc, Drawing drawing, Loop loop, Layer layer, Vector startPos)
|
||||||
|
{
|
||||||
|
var pos = startPos;
|
||||||
|
|
||||||
foreach (var code in loop)
|
foreach (var code in loop)
|
||||||
{
|
{
|
||||||
@@ -35,6 +65,8 @@ public static class DrawingDxfExporter
|
|||||||
|
|
||||||
case CodeType.LinearMove:
|
case CodeType.LinearMove:
|
||||||
var lm = (LinearMove)code;
|
var lm = (LinearMove)code;
|
||||||
|
if (lm.Type == EntityType.Cut)
|
||||||
|
{
|
||||||
var lineEnd = Advance(pos, lm.EndPoint, loop.Mode);
|
var lineEnd = Advance(pos, lm.EndPoint, loop.Mode);
|
||||||
doc.Entities.Add(new AcadLine
|
doc.Entities.Add(new AcadLine
|
||||||
{
|
{
|
||||||
@@ -43,6 +75,11 @@ public static class DrawingDxfExporter
|
|||||||
Layer = layer
|
Layer = layer
|
||||||
});
|
});
|
||||||
pos = lineEnd;
|
pos = lineEnd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos = Advance(pos, lm.EndPoint, loop.Mode);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CodeType.CircularMove:
|
case CodeType.CircularMove:
|
||||||
@@ -58,11 +95,9 @@ public static class DrawingDxfExporter
|
|||||||
var endAngle = Math.Atan2(arcEnd.Y - arcCenter.Y, arcEnd.X - arcCenter.X);
|
var endAngle = Math.Atan2(arcEnd.Y - arcCenter.Y, arcEnd.X - arcCenter.X);
|
||||||
|
|
||||||
if (cm.Rotation == RotationType.CW)
|
if (cm.Rotation == RotationType.CW)
|
||||||
{
|
|
||||||
(startAngle, endAngle) = (endAngle, startAngle);
|
(startAngle, endAngle) = (endAngle, startAngle);
|
||||||
}
|
|
||||||
|
|
||||||
if (Math.Abs(startAngle - endAngle) < 1e-10) // full circle
|
if (Math.Abs(startAngle - endAngle) < 1e-10)
|
||||||
{
|
{
|
||||||
doc.Entities.Add(new AcadCircle
|
doc.Entities.Add(new AcadCircle
|
||||||
{
|
{
|
||||||
@@ -77,26 +112,33 @@ public static class DrawingDxfExporter
|
|||||||
{
|
{
|
||||||
Center = ToXYZ(arcCenter),
|
Center = ToXYZ(arcCenter),
|
||||||
Radius = radius,
|
Radius = radius,
|
||||||
StartAngle = startAngle * (180.0 / Math.PI),
|
StartAngle = startAngle,
|
||||||
EndAngle = endAngle * (180.0 / Math.PI),
|
EndAngle = endAngle,
|
||||||
Layer = layer
|
Layer = layer
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = arcEnd;
|
pos = arcEnd;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
case CodeType.SubProgramCall:
|
||||||
|
var call = (SubProgramCall)code;
|
||||||
|
var subLoop = drawing.Loops.FirstOrDefault(l => l.Name == drawing.GetLoopName(call.LoopId));
|
||||||
|
if (subLoop != null)
|
||||||
|
DrawLoop(doc, drawing, subLoop, layer, pos);
|
||||||
|
// pos unchanged — subprograms are closed shapes that return to start
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = new MemoryStream();
|
return pos;
|
||||||
using (var writer = new DxfWriter(buffer, doc, false))
|
}
|
||||||
|
|
||||||
|
private static int GetLoopNumber(string loopName)
|
||||||
{
|
{
|
||||||
writer.Write();
|
var idx = loopName.LastIndexOf(".loop-", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
if (idx < 0) return -1;
|
||||||
|
return int.TryParse(loopName.Substring(idx + 6), out var n) ? n : -1;
|
||||||
var stream = new MemoryStream(buffer.ToArray());
|
|
||||||
return stream;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Vector Advance(Vector current, Vector offset, ProgrammingMode mode) =>
|
private static Vector Advance(Vector current, Vector offset, ProgrammingMode mode) =>
|
||||||
@@ -104,4 +146,3 @@ public static class DrawingDxfExporter
|
|||||||
|
|
||||||
private static XYZ ToXYZ(Vector v) => new XYZ(v.X, v.Y, 0);
|
private static XYZ ToXYZ(Vector v) => new XYZ(v.X, v.Y, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ namespace PepLib.IO
|
|||||||
|
|
||||||
stream.Seek(0x9, SeekOrigin.Current);
|
stream.Seek(0x9, SeekOrigin.Current);
|
||||||
|
|
||||||
Info.MaterialNumber = int.Parse(ReadString(0x40, ref stream));
|
int.TryParse(ReadString(0x40, ref stream), out var materialNumber);
|
||||||
|
Info.MaterialNumber = materialNumber;
|
||||||
Info.MaterialGrade = ReadString(0x10, ref stream);
|
Info.MaterialGrade = ReadString(0x10, ref stream);
|
||||||
Info.ProgrammedBy = ReadString(0x40, ref stream);
|
Info.ProgrammedBy = ReadString(0x40, ref stream);
|
||||||
Info.CreatedBy = ReadString(0x40, ref stream);
|
Info.CreatedBy = ReadString(0x40, ref stream);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using PepLib.Models;
|
using PepLib.Models;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
@@ -38,6 +37,11 @@ namespace PepLib.IO
|
|||||||
LoadInfo(memstream);
|
LoadInfo(memstream);
|
||||||
memstream.Close();
|
memstream.Close();
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
case ".loop-info":
|
||||||
|
case ".cadsnapshot":
|
||||||
|
memstream.Close();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Regex.IsMatch(extension, "loop-\\d\\d\\d"))
|
if (Regex.IsMatch(extension, "loop-\\d\\d\\d"))
|
||||||
@@ -47,6 +51,9 @@ namespace PepLib.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Drawing.Info == null)
|
||||||
|
Drawing.Info = DeriveInfoFromLoops();
|
||||||
|
|
||||||
Drawing.ResolveLoops();
|
Drawing.ResolveLoops();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +69,7 @@ namespace PepLib.IO
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
stream = new FileStream(nestFile, FileMode.Open);
|
stream = new FileStream(nestFile, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
Read(stream);
|
Read(stream);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -73,16 +80,23 @@ namespace PepLib.IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void LoadInfo(Stream stream)
|
private void LoadInfo(Stream stream)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
Drawing.Info = DrawingInfo.Load(stream);
|
Drawing.Info = DrawingInfo.Load(stream);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
|
||||||
|
private DrawingInfo DeriveInfoFromLoops()
|
||||||
{
|
{
|
||||||
Debug.WriteLine(exception.Message);
|
var info = new DrawingInfo();
|
||||||
Debug.WriteLine(exception.StackTrace);
|
|
||||||
|
if (Drawing.Loops.Count > 0)
|
||||||
|
{
|
||||||
|
// Loop names follow the pattern "{DrawingName}.loop-XXX"
|
||||||
|
var loopName = Drawing.Loops[0].Name;
|
||||||
|
var suffix = loopName.LastIndexOf(".loop-", StringComparison.OrdinalIgnoreCase);
|
||||||
|
info.Name = suffix >= 0 ? loopName.Substring(0, suffix) : loopName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Loop ReadLoop(string name, Stream stream)
|
private Loop ReadLoop(string name, Stream stream)
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ namespace PepLib.Models
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetLoopName(int loopId)
|
public string GetLoopName(int loopId)
|
||||||
{
|
{
|
||||||
return string.Format("{0}.loop-{1}", Info.Name, loopId.ToString().PadLeft(3, '0'));
|
return string.Format("{0}.loop-{1}", Info.Name, loopId.ToString().PadLeft(3, '0'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace PepLib.Utilities
|
|||||||
var nameList = new List<string>();
|
var nameList = new List<string>();
|
||||||
var streamList = new List<Stream>();
|
var streamList = new List<Stream>();
|
||||||
|
|
||||||
using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read))
|
using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||||
using (var zip = new ZipArchive(fileStream, ZipArchiveMode.Read))
|
using (var zip = new ZipArchive(fileStream, ZipArchiveMode.Read))
|
||||||
{
|
{
|
||||||
foreach (var entry in zip.Entries)
|
foreach (var entry in zip.Entries)
|
||||||
@@ -52,7 +52,7 @@ namespace PepLib.Utilities
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool ExtractByExtension(string file, string extension, out string name, out Stream stream)
|
public static bool ExtractByExtension(string file, string extension, out string name, out Stream stream)
|
||||||
{
|
{
|
||||||
using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read))
|
using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||||
using (var zip = new ZipArchive(fileStream, ZipArchiveMode.Read))
|
using (var zip = new ZipArchive(fileStream, ZipArchiveMode.Read))
|
||||||
{
|
{
|
||||||
foreach (var entry in zip.Entries)
|
foreach (var entry in zip.Entries)
|
||||||
|
|||||||
Reference in New Issue
Block a user