eae7c066d1
- Domain: immutable entities (Resource, Recipe, Machine, Module, etc.) - Data: JSON recipe repository (34 resources, 26 recipes, Space Age) - Solver: DFS-based production planner with module support - Reporting: Console, JSON, Excel exporters - CLI: solve/list commands with Spectre.Console - Tests: 20 unit tests (xUnit) all passing - Tech: C# .NET 10, MathNet.Numerics, ClosedXML Model: GPT-4.1 (OpenAI) — executed by pi coding agent
104 lines
4.1 KiB
C#
104 lines
4.1 KiB
C#
using ClosedXML.Excel;
|
|
using FactorioCalc.Domain;
|
|
|
|
namespace FactorioCalc.Reporting;
|
|
|
|
/// <summary>
|
|
/// Exports production results to an Excel (.xlsx) file.
|
|
/// </summary>
|
|
public sealed class ExcelReporter : IReporter
|
|
{
|
|
private readonly string _outputPath;
|
|
|
|
public ExcelReporter(string outputPath)
|
|
{
|
|
_outputPath = outputPath ?? throw new ArgumentNullException(nameof(outputPath));
|
|
}
|
|
|
|
public void Report(ProductionResult result, IReadOnlyDictionary<int, Resource> resources,
|
|
IReadOnlyDictionary<int, Recipe> recipes, IReadOnlyDictionary<int, Machine> machines)
|
|
{
|
|
using var workbook = new XLWorkbook();
|
|
|
|
// Sheet 1: Recipe Executions
|
|
var execSheet = workbook.Worksheets.Add("Executions");
|
|
execSheet.Cell(1, 1).Value = "Recipe";
|
|
execSheet.Cell(1, 2).Value = "Rate (cycles/sec)";
|
|
execSheet.Cell(1, 3).Value = "Machine";
|
|
execSheet.Cell(1, 4).Value = "Machine Count";
|
|
execSheet.Cell(1, 5).Value = "Effective Speed";
|
|
execSheet.Cell(1, 6).Value = "Effective Productivity";
|
|
execSheet.Cell(1, 7).Value = "Modules";
|
|
|
|
var execHeader = execSheet.Range(1, 1, 1, 7);
|
|
execHeader.Style.Font.Bold = true;
|
|
execHeader.Style.Fill.SetBackgroundColor(XLColor.LightBlue);
|
|
|
|
int execRow = 2;
|
|
foreach (var exec in result.Executions)
|
|
{
|
|
var recipeName = recipes.TryGetValue(exec.RecipeId, out var r) ? r.Name : $"Recipe#{exec.RecipeId}";
|
|
var machineName = machines.TryGetValue(exec.MachineId, out var m) ? m.Name : $"Machine#{exec.MachineId}";
|
|
var modulesStr = string.Join(", ", exec.Modules.Select(m => m.Name));
|
|
|
|
execSheet.Cell(execRow, 1).Value = recipeName;
|
|
execSheet.Cell(execRow, 2).Value = exec.RecipeRate;
|
|
execSheet.Cell(execRow, 3).Value = machineName;
|
|
execSheet.Cell(execRow, 4).Value = exec.MachineCount;
|
|
execSheet.Cell(execRow, 5).Value = exec.EffectiveSpeed;
|
|
execSheet.Cell(execRow, 6).Value = exec.EffectiveProductivity;
|
|
execSheet.Cell(execRow, 7).Value = modulesStr;
|
|
execRow++;
|
|
}
|
|
|
|
execSheet.Columns().AdjustToContents();
|
|
|
|
// Sheet 2: Resource Flows
|
|
var flowSheet = workbook.Worksheets.Add("Resource Flows");
|
|
flowSheet.Cell(1, 1).Value = "Resource";
|
|
flowSheet.Cell(1, 2).Value = "Flow (items/sec)";
|
|
flowSheet.Cell(1, 3).Value = "Flow (items/min)";
|
|
|
|
var flowHeader = flowSheet.Range(1, 1, 1, 3);
|
|
flowHeader.Style.Font.Bold = true;
|
|
flowHeader.Style.Fill.SetBackgroundColor(XLColor.LightGreen);
|
|
|
|
int flowRow = 2;
|
|
foreach (var (resourceId, flow) in result.ResourceFlows.OrderBy(k => k.Value))
|
|
{
|
|
var resourceName = resources.TryGetValue(resourceId, out var res) ? res.Name : $"Resource#{resourceId}";
|
|
flowSheet.Cell(flowRow, 1).Value = resourceName;
|
|
flowSheet.Cell(flowRow, 2).Value = flow;
|
|
flowSheet.Cell(flowRow, 3).Value = flow * 60;
|
|
flowRow++;
|
|
}
|
|
|
|
flowSheet.Columns().AdjustToContents();
|
|
|
|
// Sheet 3: Required Inputs
|
|
var inputSheet = workbook.Worksheets.Add("Required Inputs");
|
|
inputSheet.Cell(1, 1).Value = "Resource";
|
|
inputSheet.Cell(1, 2).Value = "items/sec";
|
|
inputSheet.Cell(1, 3).Value = "items/min";
|
|
|
|
var inputHeader = inputSheet.Range(1, 1, 1, 3);
|
|
inputHeader.Style.Font.Bold = true;
|
|
inputHeader.Style.Fill.SetBackgroundColor(XLColor.Orange);
|
|
|
|
int inputRow = 2;
|
|
foreach (var (resourceId, amount) in result.RequiredInputs)
|
|
{
|
|
var resourceName = resources.TryGetValue(resourceId, out var res) ? res.Name : $"Resource#{resourceId}";
|
|
inputSheet.Cell(inputRow, 1).Value = resourceName;
|
|
inputSheet.Cell(inputRow, 2).Value = amount;
|
|
inputSheet.Cell(inputRow, 3).Value = amount * 60;
|
|
inputRow++;
|
|
}
|
|
|
|
inputSheet.Columns().AdjustToContents();
|
|
|
|
workbook.SaveAs(_outputPath);
|
|
Console.WriteLine($" [Excel] Report saved to: {_outputPath}");
|
|
}
|
|
}
|