refactor: Relocate Document, BinFileSaver, and Toolbox to proper folders
- Move Document.cs from Forms to Models namespace - Move BinFileSaver.cs and Toolbox.cs from Forms to Services namespace - Better separation of concerns between UI and business logic Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,182 +0,0 @@
|
|||||||
using CutList.Core;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace CutList.Forms
|
|
||||||
{
|
|
||||||
public class BinFileSaver
|
|
||||||
{
|
|
||||||
private readonly IEnumerable<Bin> _bins;
|
|
||||||
|
|
||||||
private int PaddingWidthOfItemLength { get; set; }
|
|
||||||
|
|
||||||
public bool OpenFileAfterSave { get; set; }
|
|
||||||
|
|
||||||
public BinFileSaver(IEnumerable<Bin> bins)
|
|
||||||
{
|
|
||||||
_bins = bins ?? throw new ArgumentNullException(nameof(bins));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveBinsToFile(string file)
|
|
||||||
{
|
|
||||||
using (var writer = new StreamWriter(file))
|
|
||||||
{
|
|
||||||
writer.AutoFlush = true;
|
|
||||||
|
|
||||||
PaddingWidthOfItemLength = _bins
|
|
||||||
.SelectMany(b => b.Items)
|
|
||||||
.Select(i => FormatHelper.ConvertToMixedFraction(i.Length).Length)
|
|
||||||
.DefaultIfEmpty(0)
|
|
||||||
.Max();
|
|
||||||
|
|
||||||
WriteHeader(writer);
|
|
||||||
|
|
||||||
var id = 1;
|
|
||||||
foreach (var bin in _bins)
|
|
||||||
{
|
|
||||||
WriteBinSummary(writer, bin, id++);
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteFinalSummary(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OpenFileAfterSave)
|
|
||||||
{
|
|
||||||
OpenFile(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OpenFile(string file)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Process.Start("notepad.exe", file);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Process.Start(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteHeader(StreamWriter writer)
|
|
||||||
{
|
|
||||||
var totalBars = _bins.Count();
|
|
||||||
var totalItems = _bins.Sum(b => b.Items.Count);
|
|
||||||
var avgUtil = _bins.Any()
|
|
||||||
? Math.Round(_bins.Average(b => b.Utilization) * 100, 1)
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
writer.WriteLine("CUT LIST");
|
|
||||||
writer.WriteLine($"Created: {DateTime.Now:g}");
|
|
||||||
writer.WriteLine();
|
|
||||||
|
|
||||||
writer.WriteLine($"Bars: {totalBars} Items: {totalItems} Avg utilization: {avgUtil:0.0}%");
|
|
||||||
writer.WriteLine(new string('─', 80));
|
|
||||||
writer.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteBinSummary(StreamWriter writer, Bin bin, int id)
|
|
||||||
{
|
|
||||||
var stockLength = FormatHelper.ConvertToMixedFraction(bin.Length);
|
|
||||||
var dropLength = FormatHelper.ConvertToMixedFraction(bin.RemainingLength);
|
|
||||||
var usedLengthValue = bin.Length - bin.RemainingLength;
|
|
||||||
var usedLength = FormatHelper.ConvertToMixedFraction(usedLengthValue);
|
|
||||||
var utilization = Math.Round(bin.Utilization * 100, 2);
|
|
||||||
|
|
||||||
var barLabel = $"BAR {id:000}";
|
|
||||||
|
|
||||||
writer.WriteLine(barLabel);
|
|
||||||
writer.WriteLine(
|
|
||||||
$"Stock: {stockLength} Used: {usedLength} Drop: {dropLength} " +
|
|
||||||
$"Items: {bin.Items.Count} Utilization: {utilization:0.##}%");
|
|
||||||
writer.WriteLine();
|
|
||||||
|
|
||||||
WriteBinItems(writer, bin);
|
|
||||||
|
|
||||||
writer.WriteLine();
|
|
||||||
writer.WriteLine(new string('─', 80));
|
|
||||||
writer.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteBinItems(StreamWriter writer, Bin bin)
|
|
||||||
{
|
|
||||||
// Group by name + length to keep same behavior as before
|
|
||||||
var groups = bin.Items
|
|
||||||
.GroupBy(i => new { i.Name, i.Length })
|
|
||||||
.OrderBy(g => g.Key.Name)
|
|
||||||
.ThenBy(g => g.Key.Length);
|
|
||||||
|
|
||||||
// Header
|
|
||||||
var lengthHeader = "Length".PadLeft(PaddingWidthOfItemLength);
|
|
||||||
writer.WriteLine($" QTY {lengthHeader} TAG");
|
|
||||||
writer.WriteLine($" --- {new string('-', PaddingWidthOfItemLength)} ----------------");
|
|
||||||
|
|
||||||
foreach (var group in groups)
|
|
||||||
{
|
|
||||||
var first = group.First();
|
|
||||||
var count = group.Count();
|
|
||||||
var length = FormatHelper
|
|
||||||
.ConvertToMixedFraction(first.Length)
|
|
||||||
.PadLeft(PaddingWidthOfItemLength);
|
|
||||||
|
|
||||||
var tag = first.Name;
|
|
||||||
|
|
||||||
writer.WriteLine($" {count,3} {length} {tag}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteFinalSummary(StreamWriter writer)
|
|
||||||
{
|
|
||||||
var totalBars = _bins.Count();
|
|
||||||
var totalItems = _bins.Sum(b => b.Items.Count);
|
|
||||||
|
|
||||||
var totalStock = _bins.Sum(b => b.Length);
|
|
||||||
var totalUsed = _bins.Sum(b => b.Length - b.RemainingLength);
|
|
||||||
var totalDrop = _bins.Sum(b => b.RemainingLength);
|
|
||||||
|
|
||||||
var avgUtil = _bins.Any()
|
|
||||||
? Math.Round(_bins.Average(b => b.Utilization) * 100, 2)
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
var best = _bins.OrderByDescending(b => b.Utilization).FirstOrDefault();
|
|
||||||
var worst = _bins.OrderBy(b => b.Utilization).FirstOrDefault();
|
|
||||||
|
|
||||||
string fmt(double v) => FormatHelper.ConvertToMixedFraction(v);
|
|
||||||
|
|
||||||
writer.WriteLine();
|
|
||||||
writer.WriteLine("FINAL SUMMARY");
|
|
||||||
writer.WriteLine(new string('═', 80));
|
|
||||||
writer.WriteLine();
|
|
||||||
|
|
||||||
writer.WriteLine($"Total bars: {totalBars}");
|
|
||||||
writer.WriteLine($"Total items: {totalItems}");
|
|
||||||
writer.WriteLine();
|
|
||||||
|
|
||||||
writer.WriteLine($"Total stock: {fmt(totalStock)}");
|
|
||||||
writer.WriteLine($"Total used: {fmt(totalUsed)}");
|
|
||||||
writer.WriteLine($"Total drop: {fmt(totalDrop)}");
|
|
||||||
writer.WriteLine($"Average util: {avgUtil}%");
|
|
||||||
writer.WriteLine();
|
|
||||||
|
|
||||||
if (best != null)
|
|
||||||
{
|
|
||||||
writer.WriteLine($"Best bar: Util {Math.Round(best.Utilization * 100, 2)}% " +
|
|
||||||
$"Drop {fmt(best.RemainingLength)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (worst != null)
|
|
||||||
{
|
|
||||||
writer.WriteLine($"Worst bar: Util {Math.Round(worst.Utilization * 100, 2)}% " +
|
|
||||||
$"Drop {fmt(worst.RemainingLength)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteLine();
|
|
||||||
writer.WriteLine(new string('═', 80));
|
|
||||||
writer.WriteLine("End of Report");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,6 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using System.Xml.Linq;
|
|
||||||
using CutList.Models;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace CutList.Forms
|
namespace CutList.Models
|
||||||
{
|
{
|
||||||
public class Document
|
public class Document
|
||||||
{
|
{
|
||||||
131
CutList/Services/BinFileSaver.cs
Normal file
131
CutList/Services/BinFileSaver.cs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
using CutList.Core;
|
||||||
|
using CutList.Core.Formatting;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace CutList.Services
|
||||||
|
{
|
||||||
|
public class BinFileSaver
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<Bin> _bins;
|
||||||
|
|
||||||
|
private int PaddingWidthOfItemLength { get; set; }
|
||||||
|
|
||||||
|
public bool OpenFileAfterSave { get; set; }
|
||||||
|
|
||||||
|
public BinFileSaver(IEnumerable<Bin> bins)
|
||||||
|
{
|
||||||
|
_bins = bins ?? throw new ArgumentNullException(nameof(bins));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveBinsToFile(string file)
|
||||||
|
{
|
||||||
|
using (var writer = new StreamWriter(file))
|
||||||
|
{
|
||||||
|
writer.AutoFlush = true;
|
||||||
|
|
||||||
|
PaddingWidthOfItemLength = _bins
|
||||||
|
.SelectMany(b => b.Items)
|
||||||
|
.Select(i => FormatHelper.ConvertToMixedFraction(i.Length).Length)
|
||||||
|
.DefaultIfEmpty(0)
|
||||||
|
.Max();
|
||||||
|
|
||||||
|
WriteHeader(writer);
|
||||||
|
|
||||||
|
var id = 1;
|
||||||
|
foreach (var bin in _bins)
|
||||||
|
{
|
||||||
|
WriteBinSummary(writer, bin, id++);
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteFinalSummary(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OpenFileAfterSave)
|
||||||
|
{
|
||||||
|
OpenFile(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenFile(string file)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Process.Start("notepad.exe", file);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Process.Start(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteHeader(StreamWriter writer)
|
||||||
|
{
|
||||||
|
var totalBars = _bins.Count();
|
||||||
|
var totalItems = _bins.Sum(b => b.Items.Count);
|
||||||
|
|
||||||
|
writer.WriteLine("CUT LIST");
|
||||||
|
writer.WriteLine($"Date: {DateTime.Now:g}");
|
||||||
|
writer.WriteLine($"Total stock bars needed: {totalBars}");
|
||||||
|
writer.WriteLine($"Total pieces to cut: {totalItems}");
|
||||||
|
writer.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteBinSummary(StreamWriter writer, Bin bin, int id)
|
||||||
|
{
|
||||||
|
var stockLength = FormatHelper.ConvertToMixedFraction(bin.Length);
|
||||||
|
var dropLength = FormatHelper.ConvertToMixedFraction(bin.RemainingLength);
|
||||||
|
|
||||||
|
writer.WriteLine(new string('─', 50));
|
||||||
|
writer.WriteLine($"BAR #{id} - Start with {stockLength}\" stock");
|
||||||
|
writer.WriteLine();
|
||||||
|
writer.WriteLine(" Cut these pieces:");
|
||||||
|
|
||||||
|
WriteBinItems(writer, bin);
|
||||||
|
|
||||||
|
writer.WriteLine();
|
||||||
|
writer.WriteLine($" Remaining drop: {dropLength}\"");
|
||||||
|
writer.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteBinItems(StreamWriter writer, Bin bin)
|
||||||
|
{
|
||||||
|
var groups = bin.Items
|
||||||
|
.GroupBy(i => new { i.Name, i.Length })
|
||||||
|
.OrderBy(g => g.Key.Name)
|
||||||
|
.ThenBy(g => g.Key.Length);
|
||||||
|
|
||||||
|
var lengthHeader = "LENGTH".PadLeft(PaddingWidthOfItemLength + 1);
|
||||||
|
writer.WriteLine($" QTY {lengthHeader} LABEL");
|
||||||
|
|
||||||
|
foreach (var group in groups)
|
||||||
|
{
|
||||||
|
var first = group.First();
|
||||||
|
var count = group.Count();
|
||||||
|
var length = FormatHelper
|
||||||
|
.ConvertToMixedFraction(first.Length)
|
||||||
|
.PadLeft(PaddingWidthOfItemLength) + "\"";
|
||||||
|
|
||||||
|
writer.WriteLine($" {count,3} {length} {first.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteFinalSummary(StreamWriter writer)
|
||||||
|
{
|
||||||
|
var totalBars = _bins.Count();
|
||||||
|
var totalItems = _bins.Sum(b => b.Items.Count);
|
||||||
|
var totalStock = _bins.Sum(b => b.Length);
|
||||||
|
var totalDrop = _bins.Sum(b => b.RemainingLength);
|
||||||
|
|
||||||
|
string fmt(double v) => FormatHelper.ConvertToMixedFraction(v);
|
||||||
|
|
||||||
|
writer.WriteLine(new string('═', 50));
|
||||||
|
writer.WriteLine("SUMMARY");
|
||||||
|
writer.WriteLine($" Stock bars needed: {totalBars}");
|
||||||
|
writer.WriteLine($" Total pieces to cut: {totalItems}");
|
||||||
|
writer.WriteLine($" Total material used: {fmt(totalStock)}\"");
|
||||||
|
writer.WriteLine($" Total drop/waste: {fmt(totalDrop)}\"");
|
||||||
|
writer.WriteLine(new string('═', 50));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace CutList.Forms
|
namespace CutList.Services
|
||||||
{
|
{
|
||||||
public class Toolbox
|
public class Toolbox
|
||||||
{
|
{
|
||||||
Reference in New Issue
Block a user