Files
OpenNest/OpenNest.IO/ProgramReader.cs
AJ Isaacs cc286dd9b7 refactor: extract OpenNest.IO class library from WinForms project
Move DxfImporter, DxfExporter, NestReader, NestWriter, ProgramReader,
and Extensions into a new OpenNest.IO class library. The WinForms project
now references OpenNest.IO instead of ACadSharp directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 15:41:07 -04:00

391 lines
11 KiB
C#

using System.Collections.Generic;
using System.IO;
using System.Text;
using OpenNest.CNC;
using OpenNest.Geometry;
namespace OpenNest.IO
{
internal sealed class ProgramReader
{
private const int BufferSize = 200;
private int codeIndex;
private CodeBlock block;
private CodeSection section;
private Program program;
private StreamReader reader;
public ProgramReader(Stream stream)
{
reader = new StreamReader(stream);
program = new Program();
}
public Program Read()
{
string line;
while ((line = reader.ReadLine()) != null)
{
block = ParseBlock(line);
ProcessCurrentBlock();
}
return program;
}
private CodeBlock ParseBlock(string line)
{
var block = new CodeBlock();
Code code = null;
for (int i = 0; i < line.Length; ++i)
{
var c = line[i];
if (char.IsLetter(c))
block.Add((code = new Code(c)));
else if (c == ':')
{
block.Add((new Code(c, line.Remove(0, i + 1).Trim())));
break;
}
else if (code != null)
code.Value += c;
}
return block;
}
private void ProcessCurrentBlock()
{
var code = GetFirstCode();
while (code != null)
{
switch (code.Id)
{
case ':':
program.Codes.Add(new Comment(code.Value));
code = GetNextCode();
break;
case 'G':
int value = int.Parse(code.Value);
switch (value)
{
case 0:
case 1:
section = CodeSection.Line;
ReadLine(value == 0);
code = GetCurrentCode();
break;
case 2:
case 3:
section = CodeSection.Arc;
ReadArc(value == 2 ? RotationType.CW : RotationType.CCW);
code = GetCurrentCode();
break;
case 65:
section = CodeSection.SubProgram;
ReadSubProgram();
code = GetCurrentCode();
break;
case 40:
program.Codes.Add(new Kerf() { Value = KerfType.None });
code = GetNextCode();
break;
case 41:
program.Codes.Add(new Kerf() { Value = KerfType.Left });
code = GetNextCode();
break;
case 42:
program.Codes.Add(new Kerf() { Value = KerfType.Right });
code = GetNextCode();
break;
case 90:
program.Mode = Mode.Absolute;
code = GetNextCode();
break;
case 91:
program.Mode = Mode.Incremental;
code = GetNextCode();
break;
default:
code = GetNextCode();
break;
}
break;
case 'F':
program.Codes.Add(new Feedrate() { Value = double.Parse(code.Value) });
code = GetNextCode();
break;
default:
code = GetNextCode();
break;
}
}
}
private void ReadLine(bool isRapid)
{
var line = new LinearMove();
double x = 0;
double y = 0;
var layer = LayerType.Cut;
while (section == CodeSection.Line)
{
var code = GetNextCode();
if (code == null)
{
section = CodeSection.Unknown;
break;
}
switch (code.Id)
{
case 'X':
x = double.Parse(code.Value);
break;
case 'Y':
y = double.Parse(code.Value);
break;
case ':':
{
var value = code.Value.Trim().ToUpper();
switch (value)
{
case "DISPLAY":
layer = LayerType.Display;
break;
case "LEADIN":
layer = LayerType.Leadin;
break;
case "LEADOUT":
layer = LayerType.Leadout;
break;
case "SCRIBE":
layer = LayerType.Scribe;
break;
}
break;
}
default:
section = CodeSection.Unknown;
break;
}
}
if (isRapid)
program.Codes.Add(new RapidMove(x, y));
else
program.Codes.Add(new LinearMove(x, y) { Layer = layer });
}
private void ReadArc(RotationType rotation)
{
double x = 0;
double y = 0;
double i = 0;
double j = 0;
var layer = LayerType.Cut;
while (section == CodeSection.Arc)
{
var code = GetNextCode();
if (code == null)
{
section = CodeSection.Unknown;
break;
}
switch (code.Id)
{
case 'X':
x = double.Parse(code.Value);
break;
case 'Y':
y = double.Parse(code.Value);
break;
case 'I':
i = double.Parse(code.Value);
break;
case 'J':
j = double.Parse(code.Value);
break;
case ':':
{
var value = code.Value.Trim().ToUpper();
switch (value)
{
case "DISPLAY":
layer = LayerType.Display;
break;
case "LEADIN":
layer = LayerType.Leadin;
break;
case "LEADOUT":
layer = LayerType.Leadout;
break;
case "SCRIBE":
layer = LayerType.Scribe;
break;
}
break;
}
default:
section = CodeSection.Unknown;
break;
}
}
program.Codes.Add(new ArcMove()
{
EndPoint = new Vector(x, y),
CenterPoint = new Vector(i, j),
Rotation = rotation,
Layer = layer
});
}
private void ReadSubProgram()
{
var p = 0;
var r = 0.0;
while (section == CodeSection.SubProgram)
{
var code = GetNextCode();
if (code == null)
{
section = CodeSection.Unknown;
break;
}
switch (code.Id)
{
case 'P':
p = int.Parse(code.Value);
break;
case 'R':
r = double.Parse(code.Value);
break;
default:
section = CodeSection.Unknown;
break;
}
}
program.Codes.Add(new SubProgramCall() { Id = p, Rotation = r });
}
private Code GetNextCode()
{
codeIndex++;
if (codeIndex >= block.Count)
return null;
return block[codeIndex];
}
private Code GetCurrentCode()
{
if (codeIndex >= block.Count)
return null;
return block[codeIndex];
}
private Code GetFirstCode()
{
if (block.Count == 0)
return null;
codeIndex = 0;
return block[codeIndex];
}
public void Close()
{
reader.Close();
}
private class Code
{
public Code(char id)
{
Id = id;
Value = string.Empty;
}
public Code(char id, string value)
{
Id = id;
Value = value;
}
public char Id { get; private set; }
public string Value { get; set; }
public override string ToString()
{
return Id + Value;
}
}
private class CodeBlock : List<Code>
{
public void Add(char id, string value)
{
Add(new Code(id, value));
}
public override string ToString()
{
var builder = new StringBuilder();
foreach (var code in this)
builder.Append(code.ToString() + " ");
return builder.ToString();
}
}
private enum CodeSection
{
Unknown,
Arc,
Line,
SubProgram
}
}
}