using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.IO.Compression; using System.Linq; using System.Text.RegularExpressions; using System.Xml; using OpenNest.CNC; using OpenNest.Geometry; using OpenNest.Math; namespace OpenNest.IO { public sealed class NestReader { private ZipArchive zipArchive; private Dictionary plateDict; private Dictionary drawingDict; private Dictionary programDict; private Dictionary plateProgramDict; private Stream stream; private Nest nest; private NestReader() { plateDict = new Dictionary(); drawingDict = new Dictionary(); programDict = new Dictionary(); plateProgramDict = new Dictionary(); nest = new Nest(); } public NestReader(string file) : this() { stream = new FileStream(file, FileMode.Open, FileAccess.Read); zipArchive = new ZipArchive(stream, ZipArchiveMode.Read); } public NestReader(Stream stream) : this() { this.stream = stream; zipArchive = new ZipArchive(stream, ZipArchiveMode.Read); } public Nest Read() { const string plateExtensionPattern = "plate-\\d\\d\\d"; const string programExtensionPattern = "program-\\d\\d\\d"; foreach (var entry in zipArchive.Entries) { var memstream = new MemoryStream(); using (var entryStream = entry.Open()) { entryStream.CopyTo(memstream); } memstream.Position = 0; switch (entry.FullName) { case "info": ReadNestInfo(memstream); continue; case "drawing-info": ReadDrawingInfo(memstream); continue; case "plate-info": ReadPlateInfo(memstream); continue; } if (Regex.IsMatch(entry.FullName, programExtensionPattern)) { ReadProgram(memstream, entry.FullName); continue; } if (Regex.IsMatch(entry.FullName, plateExtensionPattern)) { ReadPlate(memstream, entry.FullName); continue; } } LinkProgramsToDrawings(); LinkPartsToPlates(); AddPlatesToNest(); AddDrawingsToNest(); zipArchive.Dispose(); stream.Close(); return nest; } private void ReadNestInfo(Stream stream) { var reader = XmlReader.Create(stream); var spacing = new Spacing(); while (reader.Read()) { if (!reader.IsStartElement()) continue; switch (reader.Name) { case "Nest": nest.Name = reader["name"]; break; case "Units": Units units; TryParseEnum(reader.ReadString(), out units); nest.Units = units; break; case "Customer": nest.Customer = reader.ReadString(); break; case "DateCreated": nest.DateCreated = DateTime.Parse(reader.ReadString()); break; case "DateLastModified": nest.DateLastModified = DateTime.Parse(reader.ReadString()); break; case "Notes": nest.Notes = Uri.UnescapeDataString(reader.ReadString()); break; case "Size": nest.PlateDefaults.Size = OpenNest.Geometry.Size.Parse(reader.ReadString()); break; case "Thickness": nest.PlateDefaults.Thickness = double.Parse(reader.ReadString()); break; case "Quadrant": nest.PlateDefaults.Quadrant = int.Parse(reader.ReadString()); break; case "PartSpacing": nest.PlateDefaults.PartSpacing = double.Parse(reader.ReadString()); break; case "Name": nest.PlateDefaults.Material.Name = reader.ReadString(); break; case "Grade": nest.PlateDefaults.Material.Grade = reader.ReadString(); break; case "Density": nest.PlateDefaults.Material.Density = double.Parse(reader.ReadString()); break; case "Left": spacing.Left = double.Parse(reader.ReadString()); break; case "Right": spacing.Right = double.Parse(reader.ReadString()); break; case "Top": spacing.Top = double.Parse(reader.ReadString()); break; case "Bottom": spacing.Bottom = double.Parse(reader.ReadString()); break; } } reader.Close(); nest.PlateDefaults.EdgeSpacing = spacing; } private void ReadDrawingInfo(Stream stream) { var reader = XmlReader.Create(stream); Drawing drawing = null; while (reader.Read()) { if (!reader.IsStartElement()) continue; switch (reader.Name) { case "Drawing": var id = int.Parse(reader["id"]); var name = reader["name"]; drawingDict.Add(id, (drawing = new Drawing(name))); break; case "Customer": drawing.Customer = reader.ReadString(); break; case "Color": { var parts = reader.ReadString().Split(','); if (parts.Length == 3) { byte r = byte.Parse(parts[0]); byte g = byte.Parse(parts[1]); byte b = byte.Parse(parts[2]); drawing.Color = Color.FromArgb(r, g, b); } else if (parts.Length == 4) { byte a = byte.Parse(parts[0]); byte r = byte.Parse(parts[1]); byte g = byte.Parse(parts[2]); byte b = byte.Parse(parts[3]); drawing.Color = Color.FromArgb(a, r, g, b); } } break; case "Required": drawing.Quantity.Required = int.Parse(reader.ReadString()); break; case "Name": drawing.Material.Name = reader.ReadString(); break; case "Grade": drawing.Material.Grade = reader.ReadString(); break; case "Density": drawing.Material.Density = double.Parse(reader.ReadString()); break; case "Path": drawing.Source.Path = reader.ReadString(); break; case "Offset": { var parts = reader.ReadString().Split(','); if (parts.Length != 2) continue; drawing.Source.Offset = new Vector(double.Parse(parts[0]), double.Parse(parts[1])); } break; } } reader.Close(); } private void ReadPlateInfo(Stream stream) { var reader = XmlReader.Create(stream); var spacing = new Spacing(); Plate plate = null; while (reader.Read()) { if (!reader.IsStartElement()) continue; switch (reader.Name) { case "Plate": var id = int.Parse(reader["id"]); if (plate != null) plate.EdgeSpacing = spacing; plateDict.Add(id, (plate = new Plate())); break; case "Size": plate.Size = OpenNest.Geometry.Size.Parse(reader.ReadString()); break; case "Qty": plate.Quantity = int.Parse(reader.ReadString()); break; case "Thickness": plate.Thickness = double.Parse(reader.ReadString()); break; case "Quadrant": plate.Quadrant = int.Parse(reader.ReadString()); break; case "PartSpacing": plate.PartSpacing = double.Parse(reader.ReadString()); break; case "Name": plate.Material.Name = reader.ReadString(); break; case "Grade": plate.Material.Grade = reader.ReadString(); break; case "Density": plate.Material.Density = double.Parse(reader.ReadString()); break; case "Left": spacing.Left = double.Parse(reader.ReadString()); break; case "Right": spacing.Right = double.Parse(reader.ReadString()); break; case "Top": spacing.Top = double.Parse(reader.ReadString()); break; case "Bottom": spacing.Bottom = double.Parse(reader.ReadString()); break; } } if (plate != null) plate.EdgeSpacing = spacing; } private void ReadProgram(Stream stream, string name) { var id = GetProgramId(name); var reader = new ProgramReader(stream); var pgm = reader.Read(); programDict.Add(id, pgm); } private void ReadPlate(Stream stream, string name) { var id = GetPlateId(name); var reader = new ProgramReader(stream); var pgm = reader.Read(); plateProgramDict.Add(id, pgm); } private void LinkProgramsToDrawings() { foreach (var drawingItem in drawingDict) { Program pgm; if (programDict.TryGetValue(drawingItem.Key, out pgm)) drawingItem.Value.Program = pgm; } } private void LinkPartsToPlates() { foreach (var plateProgram in plateProgramDict) { var parts = CreateParts(plateProgram.Value); Plate plate; if (!plateDict.TryGetValue(plateProgram.Key, out plate)) plate = new Plate(); plate.Parts.AddRange(parts); plateDict[plateProgram.Key] = plate; } } private void AddPlatesToNest() { var plates = plateDict.OrderBy(i => i.Key).Select(i => i.Value).ToList(); nest.Plates.AddRange(plates); } private void AddDrawingsToNest() { var drawings = drawingDict.OrderBy(i => i.Key).Select(i => i.Value).ToList(); drawings.ForEach(d => nest.Drawings.Add(d)); } private List CreateParts(Program pgm) { var parts = new List(); var pos = Vector.Zero; for (int i = 0; i < pgm.Codes.Count; i++) { var code = pgm.Codes[i]; switch (code.Type) { case CodeType.RapidMove: pos = ((RapidMove)code).EndPoint; break; case CodeType.SubProgramCall: var subpgm = (SubProgramCall)code; var dwg = drawingDict[subpgm.Id]; var part = new Part(dwg); part.Rotate(Angle.ToRadians(subpgm.Rotation)); part.Offset(pos); parts.Add(part); break; } } return parts; } private int GetPlateId(string name) { return int.Parse(name.Replace("plate-", "")); } private int GetProgramId(string name) { return int.Parse(name.Replace("program-", "")); } public static T ParseEnum(string value) { return (T)Enum.Parse(typeof(T), value, true); } public static bool TryParseEnum(string value, out T e) { try { e = ParseEnum(value); return true; } catch { e = ParseEnum(typeof(T).GetEnumValues().GetValue(0).ToString()); } return false; } private enum NestInfoSection { None, DefaultPlate, Material, EdgeSpacing, Source } } }