refactor: replace CutFab API with local file export and database

Remove CutFabApiClient and DrawingSelectionForm - exports no longer
depend on an external API server. DXF/PDF files are saved directly
to a configurable output folder, and export records are persisted
to a local SQL Server database via EF Core.

Replace Color-based progress logging with LogLevel enum across all
services. Redesign MainForm with equipment/drawing filter dropdowns
populated from export history, log row coloring by severity, and
simplified startup flow in Program.cs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 11:27:04 -05:00
parent 384fceb047
commit 49051b5e64
15 changed files with 524 additions and 1785 deletions

View File

@@ -1,283 +0,0 @@
namespace ExportDXF.Forms
{
partial class DrawingSelectionForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.equipmentLabel = new System.Windows.Forms.Label();
this.equipmentComboBox = new System.Windows.Forms.ComboBox();
this.drawingLabel = new System.Windows.Forms.Label();
this.drawingComboBox = new System.Windows.Forms.ComboBox();
this.newDrawingButton = new System.Windows.Forms.Button();
this.newDrawingPanel = new System.Windows.Forms.Panel();
this.qtyNumericUpDown = new System.Windows.Forms.NumericUpDown();
this.qtyLabel = new System.Windows.Forms.Label();
this.descriptionTextBox = new System.Windows.Forms.TextBox();
this.descriptionLabel = new System.Windows.Forms.Label();
this.drawingNumberTextBox = new System.Windows.Forms.TextBox();
this.drawingNumberLabel = new System.Windows.Forms.Label();
this.currentDrawingLabel = new System.Windows.Forms.Label();
this.okButton = new System.Windows.Forms.Button();
this.cancelButton = new System.Windows.Forms.Button();
this.statusLabel = new System.Windows.Forms.Label();
this.newDrawingPanel.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.qtyNumericUpDown)).BeginInit();
this.SuspendLayout();
//
// equipmentLabel
//
this.equipmentLabel.AutoSize = true;
this.equipmentLabel.Location = new System.Drawing.Point(12, 15);
this.equipmentLabel.Name = "equipmentLabel";
this.equipmentLabel.Size = new System.Drawing.Size(82, 17);
this.equipmentLabel.TabIndex = 0;
this.equipmentLabel.Text = "Equipment #";
//
// equipmentComboBox
//
this.equipmentComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.equipmentComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.equipmentComboBox.FormattingEnabled = true;
this.equipmentComboBox.Location = new System.Drawing.Point(100, 12);
this.equipmentComboBox.Name = "equipmentComboBox";
this.equipmentComboBox.Size = new System.Drawing.Size(470, 25);
this.equipmentComboBox.TabIndex = 1;
this.equipmentComboBox.SelectedIndexChanged += new System.EventHandler(this.equipmentComboBox_SelectedIndexChanged);
//
// drawingLabel
//
this.drawingLabel.AutoSize = true;
this.drawingLabel.Location = new System.Drawing.Point(12, 48);
this.drawingLabel.Name = "drawingLabel";
this.drawingLabel.Size = new System.Drawing.Size(68, 17);
this.drawingLabel.TabIndex = 2;
this.drawingLabel.Text = "Drawing #";
//
// drawingComboBox
//
this.drawingComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.drawingComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.drawingComboBox.FormattingEnabled = true;
this.drawingComboBox.Location = new System.Drawing.Point(100, 45);
this.drawingComboBox.Name = "drawingComboBox";
this.drawingComboBox.Size = new System.Drawing.Size(370, 25);
this.drawingComboBox.TabIndex = 3;
//
// newDrawingButton
//
this.newDrawingButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.newDrawingButton.Location = new System.Drawing.Point(476, 43);
this.newDrawingButton.Name = "newDrawingButton";
this.newDrawingButton.Size = new System.Drawing.Size(94, 27);
this.newDrawingButton.TabIndex = 4;
this.newDrawingButton.Text = "New Drawing";
this.newDrawingButton.UseVisualStyleBackColor = true;
this.newDrawingButton.Click += new System.EventHandler(this.newDrawingButton_Click);
//
// newDrawingPanel
//
this.newDrawingPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.newDrawingPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.newDrawingPanel.Controls.Add(this.qtyNumericUpDown);
this.newDrawingPanel.Controls.Add(this.qtyLabel);
this.newDrawingPanel.Controls.Add(this.descriptionTextBox);
this.newDrawingPanel.Controls.Add(this.descriptionLabel);
this.newDrawingPanel.Controls.Add(this.drawingNumberTextBox);
this.newDrawingPanel.Controls.Add(this.drawingNumberLabel);
this.newDrawingPanel.Location = new System.Drawing.Point(15, 85);
this.newDrawingPanel.Name = "newDrawingPanel";
this.newDrawingPanel.Size = new System.Drawing.Size(555, 120);
this.newDrawingPanel.TabIndex = 5;
this.newDrawingPanel.Visible = false;
//
// qtyNumericUpDown
//
this.qtyNumericUpDown.Location = new System.Drawing.Point(124, 72);
this.qtyNumericUpDown.Maximum = new decimal(new int[] {
10000,
0,
0,
0});
this.qtyNumericUpDown.Minimum = new decimal(new int[] {
1,
0,
0,
0});
this.qtyNumericUpDown.Name = "qtyNumericUpDown";
this.qtyNumericUpDown.Size = new System.Drawing.Size(100, 25);
this.qtyNumericUpDown.TabIndex = 5;
this.qtyNumericUpDown.Value = new decimal(new int[] {
1,
0,
0,
0});
//
// qtyLabel
//
this.qtyLabel.AutoSize = true;
this.qtyLabel.Location = new System.Drawing.Point(10, 74);
this.qtyLabel.Name = "qtyLabel";
this.qtyLabel.Size = new System.Drawing.Size(56, 17);
this.qtyLabel.TabIndex = 4;
this.qtyLabel.Text = "Quantity";
//
// descriptionTextBox
//
this.descriptionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.descriptionTextBox.Location = new System.Drawing.Point(124, 41);
this.descriptionTextBox.Name = "descriptionTextBox";
this.descriptionTextBox.Size = new System.Drawing.Size(411, 25);
this.descriptionTextBox.TabIndex = 3;
//
// descriptionLabel
//
this.descriptionLabel.AutoSize = true;
this.descriptionLabel.Location = new System.Drawing.Point(10, 44);
this.descriptionLabel.Name = "descriptionLabel";
this.descriptionLabel.Size = new System.Drawing.Size(74, 17);
this.descriptionLabel.TabIndex = 2;
this.descriptionLabel.Text = "Description";
//
// drawingNumberTextBox
//
this.drawingNumberTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.drawingNumberTextBox.Location = new System.Drawing.Point(124, 10);
this.drawingNumberTextBox.Name = "drawingNumberTextBox";
this.drawingNumberTextBox.Size = new System.Drawing.Size(411, 25);
this.drawingNumberTextBox.TabIndex = 1;
this.drawingNumberTextBox.TextChanged += new System.EventHandler(this.drawingNumberTextBox_TextChanged);
//
// drawingNumberLabel
//
this.drawingNumberLabel.AutoSize = true;
this.drawingNumberLabel.Location = new System.Drawing.Point(10, 13);
this.drawingNumberLabel.Name = "drawingNumberLabel";
this.drawingNumberLabel.Size = new System.Drawing.Size(108, 17);
this.drawingNumberLabel.TabIndex = 0;
this.drawingNumberLabel.Text = "Drawing Number";
//
// currentDrawingLabel
//
this.currentDrawingLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.currentDrawingLabel.AutoEllipsis = true;
this.currentDrawingLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.currentDrawingLabel.Location = new System.Drawing.Point(15, 217);
this.currentDrawingLabel.Name = "currentDrawingLabel";
this.currentDrawingLabel.Size = new System.Drawing.Size(557, 41);
this.currentDrawingLabel.TabIndex = 9;
this.currentDrawingLabel.Text = "Loading active drawing...";
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.Enabled = false;
this.okButton.Location = new System.Drawing.Point(414, 273);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 30);
this.okButton.TabIndex = 6;
this.okButton.Text = "OK";
this.okButton.UseVisualStyleBackColor = true;
this.okButton.Click += new System.EventHandler(this.okButton_Click);
//
// cancelButton
//
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(495, 273);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 30);
this.cancelButton.TabIndex = 7;
this.cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true;
this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click);
//
// statusLabel
//
this.statusLabel.AutoSize = true;
this.statusLabel.Location = new System.Drawing.Point(15, 280);
this.statusLabel.Name = "statusLabel";
this.statusLabel.Size = new System.Drawing.Size(44, 17);
this.statusLabel.TabIndex = 8;
this.statusLabel.Text = "Ready";
//
// DrawingSelectionForm
//
this.AcceptButton = this.okButton;
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(584, 315);
this.Controls.Add(this.currentDrawingLabel);
this.Controls.Add(this.statusLabel);
this.Controls.Add(this.cancelButton);
this.Controls.Add(this.okButton);
this.Controls.Add(this.newDrawingPanel);
this.Controls.Add(this.newDrawingButton);
this.Controls.Add(this.drawingComboBox);
this.Controls.Add(this.drawingLabel);
this.Controls.Add(this.equipmentComboBox);
this.Controls.Add(this.equipmentLabel);
this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "DrawingSelectionForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Select Drawing - ExportDXF";
this.newDrawingPanel.ResumeLayout(false);
this.newDrawingPanel.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.qtyNumericUpDown)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label equipmentLabel;
private System.Windows.Forms.ComboBox equipmentComboBox;
private System.Windows.Forms.Label drawingLabel;
private System.Windows.Forms.ComboBox drawingComboBox;
private System.Windows.Forms.Button newDrawingButton;
private System.Windows.Forms.Panel newDrawingPanel;
private System.Windows.Forms.TextBox drawingNumberTextBox;
private System.Windows.Forms.Label drawingNumberLabel;
private System.Windows.Forms.TextBox descriptionTextBox;
private System.Windows.Forms.Label descriptionLabel;
private System.Windows.Forms.NumericUpDown qtyNumericUpDown;
private System.Windows.Forms.Label qtyLabel;
private System.Windows.Forms.Label currentDrawingLabel;
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.Label statusLabel;
}
}

View File

@@ -1,260 +0,0 @@
using ExportDXF.Services;
using System;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ExportDXF.Forms
{
public partial class DrawingSelectionForm : Form
{
private readonly ICutFabApiClient _apiClient;
private readonly ISolidWorksService _solidWorksService;
public int? SelectedDrawingId { get; private set; }
public string SelectedDrawingNumber { get; private set; }
public DrawingSelectionForm(ICutFabApiClient apiClient, ISolidWorksService solidWorksService)
{
_apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
_solidWorksService = solidWorksService ?? throw new ArgumentNullException(nameof(solidWorksService));
_solidWorksService.ActiveDocumentChanged += (s, e) => DisplayActiveDrawing();
InitializeComponent();
}
protected override async void OnLoad(EventArgs e)
{
base.OnLoad(e);
DisplayActiveDrawing();
await LoadEquipmentAsync();
}
private void DisplayActiveDrawing()
{
try
{
var activeDoc = _solidWorksService.GetActiveDocument();
if (activeDoc != null && activeDoc.DocumentType == Models.DocumentType.Drawing)
{
currentDrawingLabel.Text = $"Active Drawing: {activeDoc.Title}";
currentDrawingLabel.ForeColor = Color.Green;
}
else if (activeDoc != null)
{
currentDrawingLabel.Text = $"Active Document: {activeDoc.Title} (Not a Drawing)";
currentDrawingLabel.ForeColor = Color.Orange;
}
else
{
currentDrawingLabel.Text = "No active SolidWorks document";
currentDrawingLabel.ForeColor = Color.Gray;
}
}
catch (Exception ex)
{
currentDrawingLabel.Text = $"Error getting active document: {ex.Message}";
currentDrawingLabel.ForeColor = Color.Red;
}
}
private async Task LoadEquipmentAsync()
{
try
{
statusLabel.Text = "Loading equipment...";
statusLabel.ForeColor = Color.Black;
var equipment = await _apiClient.GetEquipmentAsync();
equipmentComboBox.DisplayMember = nameof(CutFabApiClient.ApiEquipment.EquipmentNumber);
equipmentComboBox.ValueMember = nameof(CutFabApiClient.ApiEquipment.ID);
equipmentComboBox.DataSource = equipment;
if (equipment.Count > 0)
{
equipmentComboBox.SelectedIndex = 0;
statusLabel.Text = $"Loaded {equipment.Count} equipment record(s)";
statusLabel.ForeColor = Color.Green;
}
else
{
statusLabel.Text = "No equipment found";
statusLabel.ForeColor = Color.Red;
}
}
catch (Exception ex)
{
statusLabel.Text = $"Error loading equipment: {ex.Message}";
statusLabel.ForeColor = Color.Red;
MessageBox.Show($"Failed to load equipment: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private async void equipmentComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
await LoadDrawingsAsync();
}
private async Task LoadDrawingsAsync()
{
try
{
var selectedEquipment = equipmentComboBox.SelectedItem as CutFabApiClient.ApiEquipment;
if (selectedEquipment == null)
{
drawingComboBox.DataSource = null;
return;
}
statusLabel.Text = "Loading drawings...";
statusLabel.ForeColor = Color.Black;
var drawings = await _apiClient.GetDrawingsForEquipmentAsync(selectedEquipment.ID);
drawingComboBox.DisplayMember = nameof(CutFabApiClient.ApiDrawingSummary.DrawingNumber);
drawingComboBox.ValueMember = nameof(CutFabApiClient.ApiDrawingSummary.ID);
drawingComboBox.DataSource = drawings;
if (drawings.Count > 0)
{
statusLabel.Text = $"Loaded {drawings.Count} drawing(s)";
statusLabel.ForeColor = Color.Green;
drawingComboBox.Enabled = true;
}
else
{
statusLabel.Text = "No drawings found for this equipment";
statusLabel.ForeColor = Color.DarkBlue;
drawingComboBox.Enabled = false;
}
UpdateOkButtonState();
}
catch (Exception ex)
{
statusLabel.Text = $"Error loading drawings: {ex.Message}";
statusLabel.ForeColor = Color.Red;
}
}
private void newDrawingButton_Click(object sender, EventArgs e)
{
ToggleNewDrawingMode(!newDrawingPanel.Visible);
}
private void ToggleNewDrawingMode(bool enabled)
{
newDrawingPanel.Visible = enabled;
drawingComboBox.Enabled = !enabled;
newDrawingButton.Text = enabled ? "Cancel New" : "New Drawing";
if (enabled)
{
drawingNumberTextBox.Focus();
}
UpdateOkButtonState();
}
private void drawingNumberTextBox_TextChanged(object sender, EventArgs e)
{
UpdateOkButtonState();
}
private void UpdateOkButtonState()
{
if (newDrawingPanel.Visible)
{
// Creating new drawing - require drawing number
okButton.Enabled = !string.IsNullOrWhiteSpace(drawingNumberTextBox.Text);
}
else
{
// Selecting existing drawing
okButton.Enabled = drawingComboBox.SelectedItem != null;
}
}
private async void okButton_Click(object sender, EventArgs e)
{
try
{
okButton.Enabled = false;
statusLabel.Text = "Processing...";
statusLabel.ForeColor = Color.Black;
if (newDrawingPanel.Visible)
{
// Create new drawing
await CreateNewDrawingAsync();
}
else
{
// Use existing drawing
var selectedDrawing = drawingComboBox.SelectedItem as CutFabApiClient.ApiDrawingSummary;
if (selectedDrawing != null)
{
SelectedDrawingId = selectedDrawing.ID;
SelectedDrawingNumber = selectedDrawing.DrawingNumber;
DialogResult = DialogResult.OK;
Close();
}
}
}
catch (Exception ex)
{
statusLabel.Text = $"Error: {ex.Message}";
statusLabel.ForeColor = Color.Red;
MessageBox.Show($"Operation failed: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
okButton.Enabled = true;
}
}
private async Task CreateNewDrawingAsync()
{
var selectedEquipment = equipmentComboBox.SelectedItem as CutFabApiClient.ApiEquipment;
if (selectedEquipment == null)
{
MessageBox.Show("Please select equipment first.", "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
var drawingNumber = drawingNumberTextBox.Text.Trim();
if (string.IsNullOrWhiteSpace(drawingNumber))
{
MessageBox.Show("Please enter a drawing number.", "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
statusLabel.Text = "Creating new drawing...";
statusLabel.ForeColor = Color.Black;
var response = await _apiClient.CreateDrawingWithInfoAsync(selectedEquipment.ID, drawingNumber);
if (response.Success && response.Data.HasValue)
{
SelectedDrawingId = response.Data.Value;
SelectedDrawingNumber = drawingNumber;
statusLabel.Text = "Drawing created successfully";
statusLabel.ForeColor = Color.Green;
DialogResult = DialogResult.OK;
Close();
}
else
{
var errorMsg = response.Error ?? "Unknown error occurred";
statusLabel.Text = $"Failed to create drawing: {errorMsg}";
statusLabel.ForeColor = Color.Red;
MessageBox.Show($"Failed to create drawing: {errorMsg}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
okButton.Enabled = true;
}
}
private void cancelButton_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
}
}

View File

@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -1,4 +1,4 @@
namespace ExportDXF.Forms
namespace ExportDXF.Forms
{
partial class MainForm
{
@@ -28,192 +28,169 @@
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
this.runButton = new System.Windows.Forms.Button();
this.label3 = new System.Windows.Forms.Label();
this.viewFlipDeciderBox = new System.Windows.Forms.ComboBox();
this.mainTabControl = new System.Windows.Forms.TabControl();
this.logEventsTab = new System.Windows.Forms.TabPage();
this.logEventsDataGrid = new System.Windows.Forms.DataGridView();
this.bomTab = new System.Windows.Forms.TabPage();
this.bomDataGrid = new System.Windows.Forms.DataGridView();
this.cutTemplatesTab = new System.Windows.Forms.TabPage();
this.cutTemplatesDataGrid = new System.Windows.Forms.DataGridView();
this.dwgDetailsTab = new System.Windows.Forms.TabPage();
this.drawingPdfViewer = new AxAcroPDFLib.AxAcroPDF();
this.mainTabControl.SuspendLayout();
this.logEventsTab.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.logEventsDataGrid)).BeginInit();
this.bomTab.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.bomDataGrid)).BeginInit();
this.cutTemplatesTab.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.cutTemplatesDataGrid)).BeginInit();
this.dwgDetailsTab.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.drawingPdfViewer)).BeginInit();
this.SuspendLayout();
runButton = new System.Windows.Forms.Button();
label3 = new System.Windows.Forms.Label();
viewFlipDeciderBox = new System.Windows.Forms.ComboBox();
mainTabControl = new System.Windows.Forms.TabControl();
logEventsTab = new System.Windows.Forms.TabPage();
logEventsDataGrid = new System.Windows.Forms.DataGridView();
bomTab = new System.Windows.Forms.TabPage();
bomDataGrid = new System.Windows.Forms.DataGridView();
equipmentBox = new System.Windows.Forms.ComboBox();
label1 = new System.Windows.Forms.Label();
label2 = new System.Windows.Forms.Label();
drawingNoBox = new System.Windows.Forms.ComboBox();
mainTabControl.SuspendLayout();
logEventsTab.SuspendLayout();
((System.ComponentModel.ISupportInitialize)logEventsDataGrid).BeginInit();
bomTab.SuspendLayout();
((System.ComponentModel.ISupportInitialize)bomDataGrid).BeginInit();
SuspendLayout();
//
// runButton
//
this.runButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.runButton.Location = new System.Drawing.Point(790, 13);
this.runButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.runButton.Name = "runButton";
this.runButton.Size = new System.Drawing.Size(100, 30);
this.runButton.TabIndex = 11;
this.runButton.Text = "Start";
this.runButton.UseVisualStyleBackColor = true;
this.runButton.Click += new System.EventHandler(this.button1_Click);
runButton.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
runButton.Location = new System.Drawing.Point(656, 13);
runButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
runButton.Name = "runButton";
runButton.Size = new System.Drawing.Size(100, 30);
runButton.TabIndex = 11;
runButton.Text = "Start";
runButton.UseVisualStyleBackColor = true;
runButton.Click += button1_Click;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(12, 20);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(112, 17);
this.label3.TabIndex = 2;
this.label3.Text = "View flip decider :";
label3.AutoSize = true;
label3.Location = new System.Drawing.Point(26, 46);
label3.Name = "label3";
label3.Size = new System.Drawing.Size(105, 17);
label3.TabIndex = 2;
label3.Text = "View flip decider";
//
// viewFlipDeciderBox
//
this.viewFlipDeciderBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.viewFlipDeciderBox.FormattingEnabled = true;
this.viewFlipDeciderBox.Location = new System.Drawing.Point(130, 17);
this.viewFlipDeciderBox.Name = "viewFlipDeciderBox";
this.viewFlipDeciderBox.Size = new System.Drawing.Size(375, 25);
this.viewFlipDeciderBox.TabIndex = 3;
viewFlipDeciderBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
viewFlipDeciderBox.FormattingEnabled = true;
viewFlipDeciderBox.Location = new System.Drawing.Point(137, 43);
viewFlipDeciderBox.Name = "viewFlipDeciderBox";
viewFlipDeciderBox.Size = new System.Drawing.Size(502, 25);
viewFlipDeciderBox.TabIndex = 3;
//
// mainTabControl
//
this.mainTabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.mainTabControl.Controls.Add(this.logEventsTab);
this.mainTabControl.Controls.Add(this.bomTab);
this.mainTabControl.Controls.Add(this.cutTemplatesTab);
this.mainTabControl.Controls.Add(this.dwgDetailsTab);
this.mainTabControl.Location = new System.Drawing.Point(15, 50);
this.mainTabControl.Name = "mainTabControl";
this.mainTabControl.Padding = new System.Drawing.Point(20, 5);
this.mainTabControl.SelectedIndex = 0;
this.mainTabControl.Size = new System.Drawing.Size(879, 594);
this.mainTabControl.TabIndex = 12;
mainTabControl.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
mainTabControl.Controls.Add(logEventsTab);
mainTabControl.Controls.Add(bomTab);
mainTabControl.Location = new System.Drawing.Point(15, 74);
mainTabControl.Name = "mainTabControl";
mainTabControl.Padding = new System.Drawing.Point(20, 5);
mainTabControl.SelectedIndex = 0;
mainTabControl.Size = new System.Drawing.Size(741, 586);
mainTabControl.TabIndex = 12;
//
// logEventsTab
//
this.logEventsTab.Controls.Add(this.logEventsDataGrid);
this.logEventsTab.Location = new System.Drawing.Point(4, 30);
this.logEventsTab.Name = "logEventsTab";
this.logEventsTab.Padding = new System.Windows.Forms.Padding(3);
this.logEventsTab.Size = new System.Drawing.Size(871, 560);
this.logEventsTab.TabIndex = 0;
this.logEventsTab.Text = "Log Events";
this.logEventsTab.UseVisualStyleBackColor = true;
logEventsTab.Controls.Add(logEventsDataGrid);
logEventsTab.Location = new System.Drawing.Point(4, 30);
logEventsTab.Name = "logEventsTab";
logEventsTab.Padding = new System.Windows.Forms.Padding(3);
logEventsTab.Size = new System.Drawing.Size(733, 552);
logEventsTab.TabIndex = 0;
logEventsTab.Text = "Log Events";
logEventsTab.UseVisualStyleBackColor = true;
//
// logEventsDataGrid
//
this.logEventsDataGrid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.logEventsDataGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.logEventsDataGrid.Location = new System.Drawing.Point(6, 6);
this.logEventsDataGrid.Name = "logEventsDataGrid";
this.logEventsDataGrid.Size = new System.Drawing.Size(859, 548);
this.logEventsDataGrid.TabIndex = 0;
logEventsDataGrid.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
logEventsDataGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
logEventsDataGrid.GridColor = System.Drawing.Color.WhiteSmoke;
logEventsDataGrid.Location = new System.Drawing.Point(6, 6);
logEventsDataGrid.Name = "logEventsDataGrid";
logEventsDataGrid.Size = new System.Drawing.Size(721, 540);
logEventsDataGrid.TabIndex = 0;
//
// bomTab
//
this.bomTab.Controls.Add(this.bomDataGrid);
this.bomTab.Location = new System.Drawing.Point(4, 30);
this.bomTab.Name = "bomTab";
this.bomTab.Padding = new System.Windows.Forms.Padding(3);
this.bomTab.Size = new System.Drawing.Size(871, 560);
this.bomTab.TabIndex = 1;
this.bomTab.Text = "Bill Of Materials";
this.bomTab.UseVisualStyleBackColor = true;
bomTab.Controls.Add(bomDataGrid);
bomTab.Location = new System.Drawing.Point(4, 30);
bomTab.Name = "bomTab";
bomTab.Padding = new System.Windows.Forms.Padding(3);
bomTab.Size = new System.Drawing.Size(733, 552);
bomTab.TabIndex = 1;
bomTab.Text = "Bill Of Materials";
bomTab.UseVisualStyleBackColor = true;
//
// bomDataGrid
//
this.bomDataGrid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.bomDataGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.bomDataGrid.Location = new System.Drawing.Point(6, 6);
this.bomDataGrid.Name = "bomDataGrid";
this.bomDataGrid.Size = new System.Drawing.Size(859, 548);
this.bomDataGrid.TabIndex = 1;
bomDataGrid.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
bomDataGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
bomDataGrid.GridColor = System.Drawing.Color.WhiteSmoke;
bomDataGrid.Location = new System.Drawing.Point(6, 6);
bomDataGrid.Name = "bomDataGrid";
bomDataGrid.Size = new System.Drawing.Size(721, 540);
bomDataGrid.TabIndex = 1;
//
// cutTemplatesTab
// equipmentBox
//
this.cutTemplatesTab.Controls.Add(this.cutTemplatesDataGrid);
this.cutTemplatesTab.Location = new System.Drawing.Point(4, 30);
this.cutTemplatesTab.Name = "cutTemplatesTab";
this.cutTemplatesTab.Padding = new System.Windows.Forms.Padding(3);
this.cutTemplatesTab.Size = new System.Drawing.Size(871, 560);
this.cutTemplatesTab.TabIndex = 3;
this.cutTemplatesTab.Text = "Cut Templates";
this.cutTemplatesTab.UseVisualStyleBackColor = true;
equipmentBox.FormattingEnabled = true;
equipmentBox.Location = new System.Drawing.Point(137, 12);
equipmentBox.Name = "equipmentBox";
equipmentBox.Size = new System.Drawing.Size(166, 25);
equipmentBox.TabIndex = 13;
//
// cutTemplatesDataGrid
// label1
//
this.cutTemplatesDataGrid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.cutTemplatesDataGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.cutTemplatesDataGrid.Location = new System.Drawing.Point(6, 6);
this.cutTemplatesDataGrid.Name = "cutTemplatesDataGrid";
this.cutTemplatesDataGrid.Size = new System.Drawing.Size(859, 548);
this.cutTemplatesDataGrid.TabIndex = 2;
label1.AutoSize = true;
label1.Location = new System.Drawing.Point(61, 15);
label1.Name = "label1";
label1.Size = new System.Drawing.Size(70, 17);
label1.TabIndex = 2;
label1.Text = "Equipment";
//
// dwgDetailsTab
// label2
//
this.dwgDetailsTab.Controls.Add(this.drawingPdfViewer);
this.dwgDetailsTab.Location = new System.Drawing.Point(4, 30);
this.dwgDetailsTab.Name = "dwgDetailsTab";
this.dwgDetailsTab.Padding = new System.Windows.Forms.Padding(3);
this.dwgDetailsTab.Size = new System.Drawing.Size(871, 560);
this.dwgDetailsTab.TabIndex = 2;
this.dwgDetailsTab.Text = "Drawing Details";
this.dwgDetailsTab.UseVisualStyleBackColor = true;
label2.AutoSize = true;
label2.Location = new System.Drawing.Point(354, 15);
label2.Name = "label2";
label2.Size = new System.Drawing.Size(56, 17);
label2.TabIndex = 2;
label2.Text = "Drawing";
//
// drawingPdfViewer
// drawingNoBox
//
this.drawingPdfViewer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.drawingPdfViewer.Enabled = true;
this.drawingPdfViewer.Location = new System.Drawing.Point(6, 6);
this.drawingPdfViewer.Name = "drawingPdfViewer";
this.drawingPdfViewer.OcxState = ((System.Windows.Forms.AxHost.State)(resources.GetObject("drawingPdfViewer.OcxState")));
this.drawingPdfViewer.Size = new System.Drawing.Size(859, 548);
this.drawingPdfViewer.TabIndex = 0;
drawingNoBox.FormattingEnabled = true;
drawingNoBox.Location = new System.Drawing.Point(416, 12);
drawingNoBox.Name = "drawingNoBox";
drawingNoBox.Size = new System.Drawing.Size(223, 25);
drawingNoBox.TabIndex = 13;
//
// MainForm
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.ClientSize = new System.Drawing.Size(906, 656);
this.Controls.Add(this.mainTabControl);
this.Controls.Add(this.viewFlipDeciderBox);
this.Controls.Add(this.label3);
this.Controls.Add(this.runButton);
this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
this.MinimumSize = new System.Drawing.Size(643, 355);
this.Name = "MainForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "ExportDXF";
this.mainTabControl.ResumeLayout(false);
this.logEventsTab.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.logEventsDataGrid)).EndInit();
this.bomTab.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.bomDataGrid)).EndInit();
this.cutTemplatesTab.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.cutTemplatesDataGrid)).EndInit();
this.dwgDetailsTab.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.drawingPdfViewer)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
ClientSize = new System.Drawing.Size(768, 672);
Controls.Add(drawingNoBox);
Controls.Add(equipmentBox);
Controls.Add(mainTabControl);
Controls.Add(viewFlipDeciderBox);
Controls.Add(label2);
Controls.Add(label1);
Controls.Add(label3);
Controls.Add(runButton);
Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0);
Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
MaximizeBox = false;
MinimumSize = new System.Drawing.Size(643, 355);
Name = "MainForm";
StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
Text = "ExportDXF";
mainTabControl.ResumeLayout(false);
logEventsTab.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)logEventsDataGrid).EndInit();
bomTab.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)bomDataGrid).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
@@ -224,12 +201,11 @@
private System.Windows.Forms.TabControl mainTabControl;
private System.Windows.Forms.TabPage logEventsTab;
private System.Windows.Forms.TabPage bomTab;
private System.Windows.Forms.TabPage dwgDetailsTab;
private System.Windows.Forms.DataGridView logEventsDataGrid;
private AxAcroPDFLib.AxAcroPDF drawingPdfViewer;
private System.Windows.Forms.DataGridView bomDataGrid;
private System.Windows.Forms.TabPage cutTemplatesTab;
private System.Windows.Forms.DataGridView cutTemplatesDataGrid;
private System.Windows.Forms.ComboBox equipmentBox;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.ComboBox drawingNoBox;
}
}

View File

@@ -1,47 +1,50 @@
using ExportDXF.Data;
using ExportDXF.Extensions;
using ExportDXF.Models;
using ExportDXF.Services;
using ExportDXF.ViewFlipDeciders;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ExportDXF.Forms
{
public partial class MainForm : Form
{
private readonly ISolidWorksService _solidWorksService;
private readonly IDxfExportService _exportService;
private readonly ICutFabApiClient _apiClient;
private readonly IFileExportService _fileExportService;
private readonly Func<ExportDxfDbContext> _dbContextFactory;
private CancellationTokenSource _cancellationTokenSource;
private readonly BindingList<LogEvent> _logEvents;
private readonly BindingList<BomItem> _bomItems;
private readonly BindingList<CutFabApiClient.ApiCutTemplate> _cutTemplates;
private readonly int _selectedDrawingId;
private readonly string _selectedDrawingNumber;
private List<DrawingInfo> _allDrawings;
public MainForm(ISolidWorksService solidWorksService, IDxfExportService exportService, ICutFabApiClient apiClient, int selectedDrawingId, string selectedDrawingNumber)
public MainForm(ISolidWorksService solidWorksService, IDxfExportService exportService, IFileExportService fileExportService, Func<ExportDxfDbContext> dbContextFactory = null)
{
InitializeComponent();
_solidWorksService = solidWorksService ??
throw new ArgumentNullException(nameof(solidWorksService));
_solidWorksService.ActiveDocumentChanged += OnActiveDocumentChanged;
_exportService = exportService ??
throw new ArgumentNullException(nameof(exportService));
_apiClient = apiClient ??
throw new ArgumentNullException(nameof(apiClient));
_selectedDrawingId = selectedDrawingId;
_selectedDrawingNumber = selectedDrawingNumber ?? throw new ArgumentNullException(nameof(selectedDrawingNumber));
_fileExportService = fileExportService ??
throw new ArgumentNullException(nameof(fileExportService));
_dbContextFactory = dbContextFactory ?? (() => new ExportDxfDbContext());
_logEvents = new BindingList<LogEvent>();
_bomItems = new BindingList<BomItem>();
_cutTemplates = new BindingList<CutFabApiClient.ApiCutTemplate>();
_allDrawings = new List<DrawingInfo>();
InitializeViewFlipDeciders();
InitializeLogEventsGrid();
InitializeBomGrid();
InitializeCutTemplatesGrid();
InitializeDrawingDropdowns();
}
~MainForm()
{
_cancellationTokenSource?.Dispose();
@@ -49,12 +52,14 @@ namespace ExportDXF.Forms
components?.Dispose();
Dispose(false);
}
protected override async void OnLoad(EventArgs e)
{
base.OnLoad(e);
runButton.Enabled = false;
await InitializeAsync();
}
private async Task InitializeAsync()
{
try
@@ -62,87 +67,18 @@ namespace ExportDXF.Forms
LogMessage("Connecting to SolidWorks, this may take a minute...");
await _solidWorksService.ConnectAsync();
_solidWorksService.ActiveDocumentChanged += OnActiveDocumentChanged;
LogMessage("Ready", Color.Green);
LogMessage($"Output folder: {_fileExportService.OutputFolder}");
LogMessage("Ready");
UpdateActiveDocumentDisplay();
runButton.Enabled = true;
// Load the selected drawing's BOM items
LogMessage($"Loading BOM items for drawing {_selectedDrawingNumber}...");
await LoadBomItemsForDrawingAsync(_selectedDrawingId);
// Load cut templates
await LoadCutTemplatesAsync();
}
catch (Exception ex)
{
LogMessage($"Failed to connect to SolidWorks: {ex.Message}", Color.Red);
LogMessage($"Failed to connect to SolidWorks: {ex.Message}", LogLevel.Error);
MessageBox.Show("Failed to connect to SolidWorks.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Application.Exit();
}
}
private async Task LoadBomItemsForDrawingAsync(int drawingId)
{
try
{
_bomItems.Clear();
var apiBomItems = await _apiClient.GetBomItemsForDrawingAsync(drawingId);
foreach (var apiItem in apiBomItems)
{
var bomItem = new BomItem
{
ID = apiItem.ID,
ItemNo = apiItem.ItemNo ?? "",
PartNo = apiItem.PartNo ?? "",
SortOrder = apiItem.SortOrder,
Qty = apiItem.Qty,
TotalQty = apiItem.TotalQty,
Description = apiItem.Description ?? "",
PartName = apiItem.PartName ?? "",
ConfigurationName = apiItem.ConfigurationName ?? "",
Material = apiItem.Material ?? "",
DrawingID = apiItem.DrawingID,
CutTemplateID = apiItem.CutTemplateID,
CutTemplateName = apiItem.CutTemplateName ?? "",
Thickness = apiItem.Thickness,
KFactor = apiItem.KFactor,
DefaultBendRadius = apiItem.DefaultBendRadius
};
_bomItems.Add(bomItem);
}
LogMessage($"Loaded {apiBomItems.Count} BOM item(s)", Color.Green);
}
catch (Exception ex)
{
LogMessage($"Failed to load BOM items: {ex.Message}", Color.Red);
}
}
private async Task LoadCutTemplatesAsync()
{
try
{
_cutTemplates.Clear();
LogMessage("Loading cut templates...");
var apiCutTemplates = await _apiClient.GetCutTemplatesAsync();
foreach (var template in apiCutTemplates)
{
_cutTemplates.Add(template);
}
LogMessage($"Loaded {apiCutTemplates.Count} cut template(s)", Color.Green);
}
catch (Exception ex)
{
LogMessage($"Failed to load cut templates: {ex.Message}", Color.Red);
}
}
private void InitializeViewFlipDeciders()
{
@@ -163,6 +99,7 @@ namespace ExportDXF.Forms
viewFlipDeciderBox.DataSource = items;
viewFlipDeciderBox.DisplayMember = "Name";
}
private void InitializeLogEventsGrid()
{
// Clear any existing columns first
@@ -191,6 +128,13 @@ namespace ExportDXF.Forms
Width = 70
});
logEventsDataGrid.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = nameof(LogEvent.Part),
HeaderText = "File",
Width = 180
});
logEventsDataGrid.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = nameof(LogEvent.Message),
@@ -198,6 +142,9 @@ namespace ExportDXF.Forms
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
});
// Add row coloring based on log level
logEventsDataGrid.CellFormatting += LogEventsDataGrid_CellFormatting;
// Set the data source AFTER adding columns
logEventsDataGrid.DataSource = _logEvents;
}
@@ -264,84 +211,85 @@ namespace ExportDXF.Forms
Width = 120
});
bomDataGrid.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = nameof(BomItem.CutTemplateName),
HeaderText = "Cut Template",
Width = 120
});
// Set the data source AFTER adding columns
bomDataGrid.DataSource = _bomItems;
}
private void InitializeCutTemplatesGrid()
private void InitializeDrawingDropdowns()
{
// Clear any existing columns first
cutTemplatesDataGrid.Columns.Clear();
// Configure grid settings
cutTemplatesDataGrid.AutoGenerateColumns = false;
cutTemplatesDataGrid.AllowUserToAddRows = false;
cutTemplatesDataGrid.AllowUserToDeleteRows = false;
cutTemplatesDataGrid.ReadOnly = true;
cutTemplatesDataGrid.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
// Add columns
cutTemplatesDataGrid.Columns.Add(new DataGridViewTextBoxColumn
try
{
DataPropertyName = nameof(CutFabApiClient.ApiCutTemplate.Name),
HeaderText = "Name",
Width = 200
});
using (var db = _dbContextFactory())
{
// Get all drawing numbers from the database
var drawingNumbers = db.ExportRecords
.Select(r => r.DrawingNumber)
.Where(d => !string.IsNullOrEmpty(d))
.Distinct()
.ToList();
cutTemplatesDataGrid.Columns.Add(new DataGridViewTextBoxColumn
// Parse into DrawingInfo objects
_allDrawings = drawingNumbers
.Select(DrawingInfo.Parse)
.Where(d => d != null)
.Distinct()
.OrderBy(d => d.EquipmentNo)
.ThenBy(d => d.DrawingNo)
.ToList();
// Get distinct equipment numbers
var equipmentNumbers = _allDrawings
.Select(d => d.EquipmentNo)
.Distinct()
.OrderBy(e => e)
.ToList();
// Populate equipment dropdown
equipmentBox.Items.Clear();
equipmentBox.Items.Add(""); // Empty option for "all"
foreach (var eq in equipmentNumbers)
{
equipmentBox.Items.Add(eq);
}
// Populate drawing dropdown with all drawings initially
UpdateDrawingDropdown();
// Wire up event handler for equipment selection change
equipmentBox.SelectedIndexChanged += EquipmentBox_SelectedIndexChanged;
}
}
catch (Exception ex)
{
DataPropertyName = nameof(CutFabApiClient.ApiCutTemplate.Material),
HeaderText = "Material",
Width = 120
});
// Database might not exist yet - that's OK
System.Diagnostics.Debug.WriteLine($"Failed to load drawings from database: {ex.Message}");
}
}
cutTemplatesDataGrid.Columns.Add(new DataGridViewTextBoxColumn
private void EquipmentBox_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateDrawingDropdown();
}
private void UpdateDrawingDropdown()
{
var selectedEquipment = equipmentBox.SelectedItem?.ToString();
var filteredDrawings = string.IsNullOrEmpty(selectedEquipment)
? _allDrawings
: _allDrawings.Where(d => d.EquipmentNo == selectedEquipment).ToList();
drawingNoBox.Items.Clear();
drawingNoBox.Items.Add(""); // Empty option
foreach (var drawing in filteredDrawings)
{
DataPropertyName = nameof(CutFabApiClient.ApiCutTemplate.Thickness),
HeaderText = "Thickness",
Width = 80,
DefaultCellStyle = new DataGridViewCellStyle { Format = "0.##" }
});
drawingNoBox.Items.Add(drawing.DrawingNo);
}
cutTemplatesDataGrid.Columns.Add(new DataGridViewTextBoxColumn
if (drawingNoBox.Items.Count > 0)
{
DataPropertyName = nameof(CutFabApiClient.ApiCutTemplate.KFactor),
HeaderText = "K-Factor",
Width = 80,
DefaultCellStyle = new DataGridViewCellStyle { Format = "0.####" }
});
cutTemplatesDataGrid.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = nameof(CutFabApiClient.ApiCutTemplate.DefaultBendRadius),
HeaderText = "Bend Radius",
Width = 90,
DefaultCellStyle = new DataGridViewCellStyle { Format = "0.##" }
});
cutTemplatesDataGrid.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = nameof(CutFabApiClient.ApiCutTemplate.Version),
HeaderText = "Version",
Width = 70
});
cutTemplatesDataGrid.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = nameof(CutFabApiClient.ApiCutTemplate.Description),
HeaderText = "Description",
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
});
// Set the data source AFTER adding columns
cutTemplatesDataGrid.DataSource = _cutTemplates;
drawingNoBox.SelectedIndex = 0;
}
}
private async void button1_Click(object sender, EventArgs e)
@@ -355,6 +303,7 @@ namespace ExportDXF.Forms
await StartExportAsync();
}
}
private async Task StartExportAsync()
{
try
@@ -362,33 +311,47 @@ namespace ExportDXF.Forms
_cancellationTokenSource = new CancellationTokenSource();
var token = _cancellationTokenSource.Token;
UpdateUIForExportStart();
var activeDoc = _solidWorksService.GetActiveDocument();
if (activeDoc == null)
{
LogMessage("No active document.", Color.Red);
LogMessage("No active document.", LogLevel.Error);
return;
}
// Parse drawing number from active document title
var drawingInfo = DrawingInfo.Parse(activeDoc.Title);
var filePrefix = drawingInfo != null ? $"{drawingInfo.EquipmentNo} {drawingInfo.DrawingNo}" : activeDoc.Title;
var viewFlipDecider = GetSelectedViewFlipDecider();
var exportContext = new ExportContext
{
ActiveDocument = activeDoc,
ViewFlipDecider = viewFlipDecider,
FilePrefix = _selectedDrawingNumber,
EquipmentId = null, // Not needed for export
FilePrefix = filePrefix,
EquipmentId = null,
CancellationToken = token,
ProgressCallback = LogMessage
ProgressCallback = (msg, level, file) => LogMessage(msg, level, file),
BomItemCallback = AddBomItem
};
// Clear previous BOM items
_bomItems.Clear();
LogMessage($"Started at {DateTime.Now:t}");
LogMessage($"Exporting to: {_fileExportService.OutputFolder}");
await Task.Run(() => _exportService.Export(exportContext), token);
LogMessage("Done.", Color.Green);
LogMessage("Done.");
}
catch (OperationCanceledException)
{
LogMessage("Export canceled.", Color.Red);
LogMessage("Export canceled.", LogLevel.Error);
}
catch (Exception ex)
{
LogMessage($"Export failed: {ex.Message}", Color.Red);
LogMessage($"Export failed: {ex.Message}", LogLevel.Error);
MessageBox.Show($"Export failed: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
@@ -398,27 +361,32 @@ namespace ExportDXF.Forms
_cancellationTokenSource = null;
}
}
private void CancelExport()
{
runButton.Enabled = false;
_cancellationTokenSource?.Cancel();
}
private IViewFlipDecider GetSelectedViewFlipDecider()
{
var item = viewFlipDeciderBox.SelectedItem as ViewFlipDeciderComboboxItem;
return item?.ViewFlipDecider;
}
private void UpdateUIForExportStart()
{
viewFlipDeciderBox.Enabled = false;
runButton.Image = Properties.Resources.stop_alt;
runButton.Text = "Stop";
}
private void UpdateUIForExportComplete()
{
viewFlipDeciderBox.Enabled = true;
runButton.Image = Properties.Resources.play;
runButton.Text = "Start";
runButton.Enabled = true;
}
private void OnActiveDocumentChanged(object sender, EventArgs e)
{
if (InvokeRequired)
@@ -428,20 +396,17 @@ namespace ExportDXF.Forms
}
UpdateActiveDocumentDisplay();
}
private void UpdateActiveDocumentDisplay()
{
var activeDoc = _solidWorksService.GetActiveDocument();
var docTitle = activeDoc?.Title ?? "No Document Open";
this.Text = $"ExportDXF - {docTitle}";
}
private void LogMessage(string message, Color? color = null)
{
var level = color.HasValue && color.Value == Color.Red ? LogLevel.Error :
color.HasValue && color.Value == Color.Green ? LogLevel.Info :
color.HasValue && color.Value == Color.DarkBlue ? LogLevel.Warning :
LogLevel.Info;
AddLogEvent(level, LogAction.Start, message);
private void LogMessage(string message, LogLevel level = LogLevel.Info, string file = null)
{
AddLogEvent(level, LogAction.Start, message, part: file);
}
private void AddLogEvent(LogLevel level, LogAction action, string message, string equipment = "", string drawing = "", string part = "", string target = "", string result = "OK", int durationMs = 0)
@@ -474,5 +439,40 @@ namespace ExportDXF.Forms
logEventsDataGrid.FirstDisplayedScrollingRowIndex = logEventsDataGrid.Rows.Count - 1;
}
}
public void AddBomItem(BomItem item)
{
if (InvokeRequired)
{
Invoke(new Action(() => AddBomItem(item)));
return;
}
_bomItems.Add(item);
}
private void LogEventsDataGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.RowIndex < 0 || e.RowIndex >= _logEvents.Count)
return;
var logEvent = _logEvents[e.RowIndex];
var row = logEventsDataGrid.Rows[e.RowIndex];
switch (logEvent.Level)
{
case LogLevel.Warning:
row.DefaultCellStyle.BackColor = Color.LightGoldenrodYellow;
row.DefaultCellStyle.ForeColor = Color.DarkOrange;
break;
case LogLevel.Error:
row.DefaultCellStyle.BackColor = Color.MistyRose;
row.DefaultCellStyle.ForeColor = Color.DarkRed;
break;
default:
row.DefaultCellStyle.BackColor = Color.White;
row.DefaultCellStyle.ForeColor = Color.Black;
break;
}
}
}
}
}

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@@ -117,12 +117,4 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="drawingPdfViewer.OcxState" mimetype="application/x-microsoft.net.object.binary.base64">
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACFTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5BeEhvc3QrU3RhdGUBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAIQAAAAIB
AAAAAQAAAAAAAAAAAAAAAAwAAAAADgAASGsAANA3AAAL
</value>
</data>
</root>

View File

@@ -14,14 +14,29 @@ namespace ExportDXF.Models
public string PartName { get; set; } = "";
public string ConfigurationName { get; set; } = "";
public string Material { get; set; } = "";
public int DrawingID { get; set; }
public int? CutTemplateID { get; set; }
public string CutTemplateName { get; set; } = "";
public string DxfFilePath { get; set; } = "";
// Sheet metal properties
private double? _thickness;
public double? Thickness
{
get => _thickness;
set => _thickness = value.HasValue ? Math.Round(value.Value, 8) : null;
}
// Sheet metal properties from CutTemplate
public double? Thickness { get; set; }
public double? KFactor { get; set; }
public double? DefaultBendRadius { get; set; }
private double? _defaultBendRadius;
public double? DefaultBendRadius
{
get => _defaultBendRadius;
set => _defaultBendRadius = value.HasValue ? Math.Round(value.Value, 8) : null;
}
// EF Core relationship to ExportRecord
public int ExportRecordId { get; set; }
public virtual ExportRecord ExportRecord { get; set; }
}
public struct Size

View File

@@ -1,8 +1,8 @@
using ExportDXF.ViewFlipDeciders;
using ExportDXF.Models;
using ExportDXF.ViewFlipDeciders;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
using System;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
@@ -44,12 +44,18 @@ namespace ExportDXF.Services
/// <summary>
/// Callback for reporting progress and status messages.
/// Parameters: message, level, file (optional)
/// </summary>
public Action<string, Color?> ProgressCallback { get; set; }
public Action<string, LogLevel, string> ProgressCallback { get; set; }
public void LogProgress(string message, Color? color = null)
/// <summary>
/// Callback for adding BOM items to the UI.
/// </summary>
public Action<BomItem> BomItemCallback { get; set; }
public void LogProgress(string message, LogLevel level = LogLevel.Info, string file = null)
{
ProgressCallback?.Invoke(message, color);
ProgressCallback?.Invoke(message, level, file);
}
public SldWorks SolidWorksApp { get; set; }
@@ -90,7 +96,7 @@ namespace ExportDXF.Services
if (SolidWorksApp == null)
{
ProgressCallback?.Invoke("Warning: Cannot cleanup template drawing - SolidWorks app not available", Color.DarkBlue);
ProgressCallback?.Invoke("Warning: Cannot cleanup template drawing - SolidWorks app not available", LogLevel.Warning, null);
TemplateDrawing = null;
return;
}
@@ -104,7 +110,7 @@ namespace ExportDXF.Services
{
// Close the document without saving
SolidWorksApp.CloseDoc(title);
ProgressCallback?.Invoke("Closed template drawing", null);
ProgressCallback?.Invoke("Closed template drawing", LogLevel.Info, null);
}
}
@@ -113,7 +119,7 @@ namespace ExportDXF.Services
}
catch (Exception ex)
{
ProgressCallback?.Invoke($"Failed to close template drawing: {ex.Message}", Color.Red);
ProgressCallback?.Invoke($"Failed to close template drawing: {ex.Message}", LogLevel.Error, null);
// Still clear the reference to prevent further issues
TemplateDrawing = null;

View File

@@ -1,8 +1,7 @@
using ExportDXF.Forms;
using ExportDXF.Forms;
using ExportDXF.Services;
using System;
using System.Configuration;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ExportDXF
@@ -18,78 +17,40 @@ namespace ExportDXF
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Simple DI setup - could use a DI container like Microsoft.Extensions.DependencyInjection
var container = new ServiceContainer();
// Show drawing selection dialog first
var drawingSelectionForm = container.ResolveDrawingSelectionAsync().GetAwaiter().GetResult();
var result = drawingSelectionForm.ShowDialog();
if (result == DialogResult.OK && drawingSelectionForm.SelectedDrawingId.HasValue)
{
// User selected a drawing, proceed to main form
var mainForm = container.Resolve<MainForm>(
drawingSelectionForm.SelectedDrawingId.Value,
drawingSelectionForm.SelectedDrawingNumber);
Application.Run(mainForm);
}
// If user cancelled, just exit
var mainForm = container.ResolveMainForm();
Application.Run(mainForm);
}
}
/// <summary>
/// Simple dependency injection container.
/// For production, consider using Microsoft.Extensions.DependencyInjection or similar.
/// </summary>
public class ServiceContainer
{
private readonly string _baseUrl;
private readonly CutFabApiClient _apiClient;
private readonly string _outputFolder;
public ServiceContainer()
{
_baseUrl = ConfigurationManager.AppSettings["CutFab.ApiBaseUrl"] ?? "http://localhost:7027";
_apiClient = new CutFabApiClient(_baseUrl);
_outputFolder = ConfigurationManager.AppSettings["ExportOutputFolder"] ?? @"C:\ExportDXF\Output";
}
public async Task<DrawingSelectionForm> ResolveDrawingSelectionAsync()
public MainForm ResolveMainForm()
{
// Connect to SolidWorks first
var solidWorksService = new SolidWorksService();
try
{
await solidWorksService.ConnectAsync();
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(
$"Warning: Could not connect to SolidWorks: {ex.Message}\n\nYou can still select a drawing, but the active document will not be displayed.",
"SolidWorks Connection Warning",
System.Windows.Forms.MessageBoxButtons.OK,
System.Windows.Forms.MessageBoxIcon.Warning);
}
return new DrawingSelectionForm(_apiClient, solidWorksService);
}
public MainForm Resolve<T>(int selectedDrawingId, string selectedDrawingNumber) where T : MainForm
{
// Create the dependency tree
var solidWorksService = new SolidWorksService();
var bomExtractor = new BomExtractor();
var partExporter = new PartExporter();
var drawingExporter = new DrawingExporter();
var fileExportService = new FileExportService(_outputFolder);
var exportService = new DxfExportService(
solidWorksService,
bomExtractor,
partExporter,
drawingExporter,
_apiClient);
fileExportService);
return new MainForm(solidWorksService, exportService, _apiClient, selectedDrawingId, selectedDrawingNumber);
return new MainForm(solidWorksService, exportService, fileExportService);
}
}
}

View File

@@ -1,9 +1,9 @@
using ExportDXF.Extensions;
using ExportDXF.ItemExtractors;
using ExportDXF.Models;
using SolidWorks.Interop.sldworks;
using System;
using System.Collections.Generic;
using System.Drawing;
namespace ExportDXF.Services
{
@@ -16,14 +16,14 @@ namespace ExportDXF.Services
/// Extracts items from all BOM tables in a drawing document.
/// </summary>
/// <param name="drawing">The drawing document containing BOM tables.</param>
/// <param name="progressCallback">Optional callback for progress updates.</param>
/// <param name="progressCallback">Optional callback for progress updates (message, level, file).</param>
/// <returns>A list of extracted items.</returns>
List<Item> ExtractFromDrawing(DrawingDoc drawing, Action<string, Color?> progressCallback);
List<Item> ExtractFromDrawing(DrawingDoc drawing, Action<string, LogLevel, string> progressCallback);
}
public class BomExtractor : IBomExtractor
{
public List<Item> ExtractFromDrawing(DrawingDoc drawing, Action<string, Color?> progressCallback)
public List<Item> ExtractFromDrawing(DrawingDoc drawing, Action<string, LogLevel, string> progressCallback)
{
if (drawing == null)
throw new ArgumentNullException(nameof(drawing));
@@ -32,11 +32,11 @@ namespace ExportDXF.Services
if (bomTables.Count == 0)
{
progressCallback?.Invoke("Error: Bill of materials not found.", Color.Red);
progressCallback?.Invoke("Error: Bill of materials not found.", LogLevel.Error, null);
return new List<Item>();
}
progressCallback?.Invoke($"Found {bomTables.Count} BOM table(s)", null);
progressCallback?.Invoke($"Found {bomTables.Count} BOM table(s)", LogLevel.Info, null);
var allItems = new List<Item>();
@@ -49,14 +49,14 @@ namespace ExportDXF.Services
SkipHiddenRows = true
};
progressCallback?.Invoke($"Fetching components from {bom.BomFeature.Name}", null);
progressCallback?.Invoke($"Fetching components from {bom.BomFeature.Name}", LogLevel.Info, null);
var items = extractor.GetItems();
allItems.AddRange(items);
}
catch (Exception ex)
{
progressCallback?.Invoke($"Failed to extract from {bom.BomFeature.Name}: {ex.Message}", Color.Red);
progressCallback?.Invoke($"Failed to extract: {ex.Message}", LogLevel.Error, bom.BomFeature.Name);
}
}

View File

@@ -1,468 +0,0 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using static ExportDXF.Services.CutFabApiClient;
namespace ExportDXF.Services
{
public interface ICutFabApiClient
{
string BaseUrl { get; }
Task<int?> ResolveDrawingIdAsync(string drawingNumber);
Task<int?> CreateDrawingAsync(int equipmentId, string drawingNumber);
Task<CutFabApiClient.ApiResponse<int?>> CreateDrawingWithInfoAsync(int equipmentId, string drawingNumber);
Task<bool> UploadDrawingPdfAsync(string drawingNumber, string pdfPath, string uploadedBy = null, string notes = null);
Task<bool> UploadDxfZipAsync(
int drawingId,
string zipPath,
double? thickness = null,
double? kfactor = null,
double? defaultBendRadius = null,
string material = null);
Task<int?> CreateBomItemAsync(object upsertBomItemDto);
Task<bool> AutoLinkTemplatesAsync(int drawingId);
Task<List<ApiEquipment>> GetEquipmentAsync();
Task<List<ApiDrawingSummary>> GetDrawingsForEquipmentAsync(int equipmentId);
Task<List<ApiBomItem>> GetBomItemsForDrawingAsync(int drawingId);
Task<List<ApiCutTemplate>> GetCutTemplatesAsync();
}
public class CutFabApiClient : ICutFabApiClient, IDisposable
{
private readonly HttpClient _http;
private readonly string _baseUrl;
public string BaseUrl => _baseUrl;
public class ApiResponse<T>
{
public bool Success { get; set; }
public int StatusCode { get; set; }
public T Data { get; set; }
public string RawBody { get; set; }
public string Error { get; set; }
}
public CutFabApiClient(string baseUrl)
{
_baseUrl = (baseUrl ?? string.Empty).TrimEnd('/');
if (string.IsNullOrWhiteSpace(_baseUrl))
{
// Default to deployed API port from deployment script
_baseUrl = "http://localhost:7027";
}
_http = new HttpClient
{
Timeout = TimeSpan.FromSeconds(100)
};
}
public async Task<int?> ResolveDrawingIdAsync(string drawingNumber)
{
try
{
var url = $"{_baseUrl}/api/Drawings/resolve?drawingNumber={Uri.EscapeDataString(drawingNumber)}";
var resp = await _http.GetAsync(url).ConfigureAwait(false);
if (!resp.IsSuccessStatusCode) return null;
var json = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
var dict = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json);
if (dict != null && dict.ContainsKey("ID"))
{
var idObj = dict["ID"]; // serializer returns int or double depending
if (idObj is int i) return i;
if (idObj is double d) return (int)d;
}
return null;
}
catch
{
return null;
}
}
public async Task<int?> CreateDrawingAsync(int equipmentId, string drawingNumber)
{
var payload = new { DrawingNumber = drawingNumber, Description = (string)null, Qty = 1, EquipmentID = equipmentId };
var json = new JavaScriptSerializer().Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var resp = await _http.PostAsync($"{_baseUrl}/api/Drawings", content).ConfigureAwait(false);
if (!resp.IsSuccessStatusCode) return null;
var body = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
try
{
var dict = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(body);
if (dict != null && dict.ContainsKey("ID"))
{
var idObj = dict["ID"]; if (idObj is int i) return i; if (idObj is double d) return (int)d;
}
}
catch { }
return null;
}
public async Task<ApiResponse<int?>> CreateDrawingWithInfoAsync(int equipmentId, string drawingNumber)
{
var result = new ApiResponse<int?> { Success = false, StatusCode = 0, Data = null, RawBody = null, Error = null };
try
{
var payload = new { DrawingNumber = drawingNumber, Description = (string)null, Qty = 1, EquipmentID = equipmentId };
var json = new JavaScriptSerializer().Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var resp = await _http.PostAsync($"{_baseUrl}/api/Drawings", content).ConfigureAwait(false);
result.StatusCode = (int)resp.StatusCode;
result.Success = resp.IsSuccessStatusCode;
var body = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
result.RawBody = body;
if (!resp.IsSuccessStatusCode)
{
result.Error = "HTTP " + ((int)resp.StatusCode) + " " + resp.ReasonPhrase;
return result;
}
try
{
var dict = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(body);
if (dict != null)
{
object v;
if (TryGetCI(dict, new[] { "ID", "id" }, out v))
{
if (v is int i) { result.Data = i; return result; }
if (v is double d) { result.Data = (int)d; return result; }
int parsed; if (int.TryParse(Convert.ToString(v), out parsed)) { result.Data = parsed; return result; }
}
}
}
catch (Exception ex)
{
result.Error = ex.Message;
}
return result;
}
catch (Exception ex)
{
result.Error = ex.Message;
return result;
}
}
public async Task<bool> UploadDrawingPdfAsync(string drawingNumber, string pdfPath, string uploadedBy = null, string notes = null)
{
if (!File.Exists(pdfPath)) return false;
using (var form = new MultipartFormDataContent())
{
form.Add(new StringContent(drawingNumber ?? string.Empty), "drawingNumber");
if (!string.IsNullOrWhiteSpace(uploadedBy)) form.Add(new StringContent(uploadedBy), "uploadedBy");
if (!string.IsNullOrWhiteSpace(notes)) form.Add(new StringContent(notes), "notes");
var fileContent = new StreamContent(File.OpenRead(pdfPath));
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/pdf");
var fileName = Path.GetFileName(pdfPath);
form.Add(fileContent, "file", fileName);
var resp = await _http.PostAsync($"{_baseUrl}/api/DrawingRevisions/upload", form).ConfigureAwait(false);
return resp.IsSuccessStatusCode;
}
}
public async Task<bool> UploadDxfZipAsync(int drawingId, string zipPath, double? thickness = null, double? kfactor = null, double? defaultBendRadius = null, string material = null)
{
if (!File.Exists(zipPath)) return false;
using (var form = new MultipartFormDataContent())
{
var fileContent = new StreamContent(File.OpenRead(zipPath));
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/zip");
var fileName = Path.GetFileName(zipPath);
form.Add(fileContent, "file", fileName);
// Add thickness and kfactor if provided
if (thickness.HasValue)
form.Add(new StringContent(thickness.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)), "thickness");
if (kfactor.HasValue)
form.Add(new StringContent(kfactor.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)), "kfactor");
// Add default bend radius and material if provided
if (defaultBendRadius.HasValue)
form.Add(new StringContent(defaultBendRadius.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)), "defaultBendRadius");
if (!string.IsNullOrWhiteSpace(material))
form.Add(new StringContent(material), "material");
var resp = await _http.PostAsync($"{_baseUrl}/api/Drawings/{drawingId}/upload-dxf-templates", form).ConfigureAwait(false);
return resp.IsSuccessStatusCode;
}
}
public async Task<int?> CreateBomItemAsync(object upsertBomItemDto)
{
var json = new JavaScriptSerializer().Serialize(upsertBomItemDto);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var resp = await _http.PostAsync($"{_baseUrl}/api/BomItems", content).ConfigureAwait(false);
if (!resp.IsSuccessStatusCode) return null;
var body = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
try
{
var dict = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(body);
if (dict != null)
{
object v;
if (TryGetCI(dict, new[] { "ID", "id" }, out v))
{
if (v is int i) return i;
if (v is double d) return (int)d;
int parsed; if (int.TryParse(Convert.ToString(v), out parsed)) return parsed;
}
}
}
catch { }
// Successful HTTP with empty/minimal body: treat as success
return 0;
}
public async Task<bool> AutoLinkTemplatesAsync(int drawingId)
{
var url = $"{_baseUrl}/api/Drawings/{drawingId}/auto-link-templates";
var resp = await _http.PostAsync(url, new ByteArrayContent(new byte[0])).ConfigureAwait(false);
return resp.IsSuccessStatusCode;
}
public async Task<List<ApiBomItem>> GetBomItemsForDrawingAsync(int drawingId)
{
var url = $"{_baseUrl}/api/BomItems/drawing/{drawingId}";
var resp = await _http.GetAsync(url).ConfigureAwait(false);
if (!resp.IsSuccessStatusCode) return new List<ApiBomItem>();
var json = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
var serializer = new JavaScriptSerializer();
var raw = serializer.DeserializeObject(json);
var result = new List<ApiBomItem>();
if (raw is System.Collections.IEnumerable enumerable && !(raw is string))
{
foreach (var item in enumerable)
{
var dict = item as Dictionary<string, object>;
if (dict == null) continue;
var bomItem = new ApiBomItem();
object v;
if (TryGetCI(dict, new[] { "ID", "id" }, out v)) bomItem.ID = ToInt(v);
if (TryGetCI(dict, new[] { "ItemNo", "itemNo" }, out v)) bomItem.ItemNo = v?.ToString();
if (TryGetCI(dict, new[] { "PartNo", "partNo" }, out v)) bomItem.PartNo = v?.ToString();
if (TryGetCI(dict, new[] { "SortOrder", "sortOrder" }, out v)) bomItem.SortOrder = ToInt(v);
if (TryGetCI(dict, new[] { "Qty", "qty" }, out v)) bomItem.Qty = v != null ? (int?)ToInt(v) : null;
if (TryGetCI(dict, new[] { "TotalQty", "totalQty" }, out v)) bomItem.TotalQty = v != null ? (int?)ToInt(v) : null;
if (TryGetCI(dict, new[] { "Description", "description" }, out v)) bomItem.Description = v?.ToString();
if (TryGetCI(dict, new[] { "PartName", "partName" }, out v)) bomItem.PartName = v?.ToString();
if (TryGetCI(dict, new[] { "ConfigurationName", "configurationName" }, out v)) bomItem.ConfigurationName = v?.ToString();
if (TryGetCI(dict, new[] { "Material", "material" }, out v)) bomItem.Material = v?.ToString();
if (TryGetCI(dict, new[] { "DrawingID", "drawingID", "drawingId" }, out v)) bomItem.DrawingID = ToInt(v);
if (TryGetCI(dict, new[] { "CutTemplateID", "cutTemplateID", "cutTemplateId" }, out v)) bomItem.CutTemplateID = v != null ? (int?)ToInt(v) : null;
// Try to get CutTemplate info if available
if (TryGetCI(dict, new[] { "CutTemplate", "cutTemplate" }, out v))
{
var templateDict = v as Dictionary<string, object>;
if (templateDict != null)
{
object tv;
if (TryGetCI(templateDict, new[] { "Name", "name" }, out tv)) bomItem.CutTemplateName = tv?.ToString();
if (TryGetCI(templateDict, new[] { "Thickness", "thickness" }, out tv)) bomItem.Thickness = ToDouble(tv);
if (TryGetCI(templateDict, new[] { "KFactor", "kFactor", "kfactor" }, out tv)) bomItem.KFactor = ToDouble(tv);
if (TryGetCI(templateDict, new[] { "DefaultBendRadius", "defaultBendRadius" }, out tv)) bomItem.DefaultBendRadius = ToDouble(tv);
}
}
result.Add(bomItem);
}
}
return result;
}
public async Task<List<ApiCutTemplate>> GetCutTemplatesAsync()
{
var url = $"{_baseUrl}/api/CutTemplates";
var resp = await _http.GetAsync(url).ConfigureAwait(false);
if (!resp.IsSuccessStatusCode) return new List<ApiCutTemplate>();
var json = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
var serializer = new JavaScriptSerializer();
var raw = serializer.DeserializeObject(json);
var result = new List<ApiCutTemplate>();
if (raw is System.Collections.IEnumerable enumerable && !(raw is string))
{
foreach (var item in enumerable)
{
var dict = item as Dictionary<string, object>;
if (dict == null) continue;
var template = new ApiCutTemplate();
object v;
if (TryGetCI(dict, new[] { "ID", "id" }, out v)) template.ID = ToInt(v);
if (TryGetCI(dict, new[] { "Name", "name" }, out v)) template.Name = v?.ToString();
if (TryGetCI(dict, new[] { "FilePath", "filePath" }, out v)) template.FilePath = v?.ToString();
if (TryGetCI(dict, new[] { "Description", "description" }, out v)) template.Description = v?.ToString();
if (TryGetCI(dict, new[] { "Material", "material" }, out v)) template.Material = v?.ToString();
if (TryGetCI(dict, new[] { "Thickness", "thickness" }, out v)) template.Thickness = ToDouble(v);
if (TryGetCI(dict, new[] { "KFactor", "kFactor", "kfactor" }, out v)) template.KFactor = ToDouble(v);
if (TryGetCI(dict, new[] { "DefaultBendRadius", "defaultBendRadius" }, out v)) template.DefaultBendRadius = ToDouble(v);
if (TryGetCI(dict, new[] { "Version", "version" }, out v)) template.Version = ToInt(v);
result.Add(template);
}
}
return result;
}
public void Dispose()
{
_http?.Dispose();
}
// Lightweight DTOs for UI binding
public class ApiEquipment
{
public int ID { get; set; }
public string EquipmentNumber { get; set; }
public string Description { get; set; }
public override string ToString() => EquipmentNumber ?? base.ToString();
}
public class ApiDrawingSummary
{
public int ID { get; set; }
public string DrawingNumber { get; set; }
public string Description { get; set; }
public override string ToString() => DrawingNumber ?? base.ToString();
}
public class ApiBomItem
{
public int ID { get; set; }
public string ItemNo { get; set; }
public string PartNo { get; set; }
public int SortOrder { get; set; }
public int? Qty { get; set; }
public int? TotalQty { get; set; }
public string Description { get; set; }
public string PartName { get; set; }
public string ConfigurationName { get; set; }
public string Material { get; set; }
public int DrawingID { get; set; }
public int? CutTemplateID { get; set; }
public string CutTemplateName { get; set; }
public double? Thickness { get; set; }
public double? KFactor { get; set; }
public double? DefaultBendRadius { get; set; }
}
public class ApiCutTemplate
{
public int ID { get; set; }
public string Name { get; set; }
public string FilePath { get; set; }
public string Description { get; set; }
public string Material { get; set; }
public double? Thickness { get; set; }
public double? KFactor { get; set; }
public double? DefaultBendRadius { get; set; }
public int Version { get; set; }
}
public async Task<List<ApiEquipment>> GetEquipmentAsync()
{
var url = $"{_baseUrl}/api/Equipment";
var req = new HttpRequestMessage(HttpMethod.Get, url);
req.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var resp = await _http.SendAsync(req).ConfigureAwait(false);
resp.EnsureSuccessStatusCode();
var json = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
var serializer = new JavaScriptSerializer();
var raw = serializer.DeserializeObject(json);
var result = new List<ApiEquipment>();
if (raw is System.Collections.IEnumerable enumerable && !(raw is string))
{
foreach (var item in enumerable)
{
var dict = item as Dictionary<string, object>;
if (dict == null) continue;
var eq = new ApiEquipment();
object v;
if (TryGetCI(dict, new[] { "ID", "id" }, out v)) eq.ID = ToInt(v);
if (TryGetCI(dict, new[] { "EquipmentNumber", "equipmentNumber", "equipmentNo" }, out v)) eq.EquipmentNumber = v?.ToString();
if (TryGetCI(dict, new[] { "Description", "description" }, out v)) eq.Description = v?.ToString();
result.Add(eq);
}
}
return result;
}
public async Task<List<ApiDrawingSummary>> GetDrawingsForEquipmentAsync(int equipmentId)
{
var url = $"{_baseUrl}/api/Equipment/{equipmentId}";
var resp = await _http.GetAsync(url).ConfigureAwait(false);
if (!resp.IsSuccessStatusCode) return new List<ApiDrawingSummary>();
var json = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
var serializer = new JavaScriptSerializer();
var root = serializer.DeserializeObject(json) as Dictionary<string, object>;
var results = new List<ApiDrawingSummary>();
if (root != null)
{
object dval;
if (!TryGetCI(root, new[] { "Drawings", "drawings" }, out dval)) return results;
if (dval is System.Collections.IEnumerable arr && !(dval is string))
{
foreach (var item in arr)
{
var d = item as Dictionary<string, object>;
if (d == null) continue;
var summary = new ApiDrawingSummary();
object v;
if (TryGetCI(d, new[] { "ID", "id" }, out v)) summary.ID = ToInt(v);
if (TryGetCI(d, new[] { "DrawingNumber", "drawingNumber" }, out v)) summary.DrawingNumber = v?.ToString();
if (TryGetCI(d, new[] { "Description", "description" }, out v)) summary.Description = v?.ToString();
results.Add(summary);
}
}
}
return results;
}
private static bool TryGetCI(Dictionary<string, object> dict, IEnumerable<string> keys, out object value)
{
foreach (var k in keys)
{
if (dict.ContainsKey(k)) { value = dict[k]; return true; }
foreach (var dk in dict.Keys)
{
if (string.Equals(dk, k, StringComparison.OrdinalIgnoreCase)) { value = dict[dk]; return true; }
}
}
value = null; return false;
}
private static int ToInt(object v)
{
if (v == null) return 0;
if (v is int i) return i;
if (v is long l) return (int)l;
if (v is double d) return (int)d;
int parsed; if (int.TryParse(v.ToString(), out parsed)) return parsed; return 0;
}
private static double? ToDouble(object v)
{
if (v == null) return null;
if (v is double d) return d;
if (v is int i) return (double)i;
if (v is long l) return (double)l;
if (v is float f) return (double)f;
double parsed; if (double.TryParse(v.ToString(), out parsed)) return parsed;
return null;
}
}
}

View File

@@ -1,7 +1,7 @@
using SolidWorks.Interop.sldworks;
using ExportDXF.Models;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
using System;
using System.Drawing;
using System.IO;
namespace ExportDXF.Services
@@ -57,7 +57,7 @@ namespace ExportDXF.Services
(int)swExportDataSheetsToExport_e.swExportData_ExportAllSheets,
drawing);
context.ProgressCallback?.Invoke($"Exporting drawing to PDF: \"{pdfFileName}\"", null);
context.ProgressCallback?.Invoke("Exporting drawing to PDF", LogLevel.Info, pdfFileName);
int errors = 0;
int warnings = 0;
@@ -73,26 +73,26 @@ namespace ExportDXF.Services
if (success && errors == 0)
{
context.ProgressCallback?.Invoke($"Saved drawing to PDF: \"{pdfFileName}\"", Color.Green);
context.ProgressCallback?.Invoke("Saved drawing to PDF", LogLevel.Info, pdfFileName);
}
else if (success && warnings > 0)
{
context.ProgressCallback?.Invoke(
$"PDF export completed with warnings: {warnings}",
Color.DarkBlue);
LogLevel.Warning, pdfFileName);
}
else
{
context.ProgressCallback?.Invoke(
$"PDF export failed. Errors: {errors}, Warnings: {warnings}",
Color.Red);
LogLevel.Error, pdfFileName);
throw new InvalidOperationException($"PDF export failed with {errors} errors and {warnings} warnings.");
}
}
catch (Exception ex)
{
var errorMessage = $"Failed to export PDF: {ex.Message}";
context.ProgressCallback?.Invoke(errorMessage, Color.Red);
context.ProgressCallback?.Invoke(errorMessage, LogLevel.Error, null);
throw new InvalidOperationException(errorMessage, ex);
}
}

View File

@@ -1,17 +1,12 @@
using ExportDXF.Data;
using ExportDXF.Extensions;
using ExportDXF.ItemExtractors;
using ExportDXF.Models;
using ExportDXF;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Environment = System.Environment;
using System.IO.Compression;
namespace ExportDXF.Services
{
@@ -33,20 +28,23 @@ namespace ExportDXF.Services
private readonly IBomExtractor _bomExtractor;
private readonly IPartExporter _partExporter;
private readonly IDrawingExporter _drawingExporter;
private readonly ICutFabApiClient _apiClient;
private readonly IFileExportService _fileExportService;
private readonly Func<ExportDxfDbContext> _dbContextFactory;
public DxfExportService(
ISolidWorksService solidWorksService,
IBomExtractor bomExtractor,
IPartExporter partExporter,
IDrawingExporter drawingExporter,
ICutFabApiClient apiClient)
IFileExportService fileExportService,
Func<ExportDxfDbContext> dbContextFactory = null)
{
_solidWorksService = solidWorksService ?? throw new ArgumentNullException(nameof(solidWorksService));
_bomExtractor = bomExtractor ?? throw new ArgumentNullException(nameof(bomExtractor));
_partExporter = partExporter ?? throw new ArgumentNullException(nameof(partExporter));
_drawingExporter = drawingExporter ?? throw new ArgumentNullException(nameof(drawingExporter));
_apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
_fileExportService = fileExportService ?? throw new ArgumentNullException(nameof(fileExportService));
_dbContextFactory = dbContextFactory ?? (() => new ExportDxfDbContext());
}
/// <summary>
@@ -81,7 +79,7 @@ namespace ExportDXF.Services
break;
default:
LogProgress(context, "Unknown document type.", Color.Red);
LogProgress(context, "Unknown document type.", LogLevel.Error);
break;
}
}
@@ -91,7 +89,7 @@ namespace ExportDXF.Services
_solidWorksService.EnableUserControl(true);
var duration = DateTime.Now - startTime;
LogProgress(context, $"Run time: {duration.ToReadableFormat()}", null);
LogProgress(context, $"Run time: {duration.ToReadableFormat()}");
}
}
@@ -99,28 +97,28 @@ namespace ExportDXF.Services
private void ExportPart(ExportContext context)
{
LogProgress(context, "Active document is a Part", null);
LogProgress(context, "Active document is a Part");
var part = context.ActiveDocument.NativeDocument as PartDoc;
if (part == null)
{
LogProgress(context, "Failed to get part document.", Color.Red);
LogProgress(context, "Failed to get part document.", LogLevel.Error);
return;
}
var tempDir = CreateTempWorkDir();
_partExporter.ExportSinglePart(part, tempDir, context);
// Export directly to the output folder
_partExporter.ExportSinglePart(part, _fileExportService.OutputFolder, context);
}
private void ExportAssembly(ExportContext context)
{
LogProgress(context, "Active document is an Assembly", null);
LogProgress(context, "Fetching components...", null);
LogProgress(context, "Active document is an Assembly");
LogProgress(context, "Fetching components...");
var assembly = context.ActiveDocument.NativeDocument as AssemblyDoc;
if (assembly == null)
{
LogProgress(context, "Failed to get assembly document.", Color.Red);
LogProgress(context, "Failed to get assembly document.", LogLevel.Error);
return;
}
@@ -128,25 +126,25 @@ namespace ExportDXF.Services
if (items == null || items.Count == 0)
{
LogProgress(context, "No items found in assembly.", Color.DarkBlue);
LogProgress(context, "No items found in assembly.", LogLevel.Warning);
return;
}
LogProgress(context, $"Found {items.Count} item(s).", null);
LogProgress(context, $"Found {items.Count} item(s).");
var tempDir = CreateTempWorkDir();
ExportItems(items, tempDir, context, drawingId: null);
// Export directly to the output folder
ExportItems(items, _fileExportService.OutputFolder, context);
}
private void ExportDrawing(ExportContext context)
{
LogProgress(context, "Active document is a Drawing", null);
LogProgress(context, "Finding BOM tables...", null);
LogProgress(context, "Active document is a Drawing");
LogProgress(context, "Finding BOM tables...");
var drawing = context.ActiveDocument.NativeDocument as DrawingDoc;
if (drawing == null)
{
LogProgress(context, "Failed to get drawing document.", Color.Red);
LogProgress(context, "Failed to get drawing document.", LogLevel.Error);
return;
}
@@ -154,126 +152,61 @@ namespace ExportDXF.Services
if (items == null || items.Count == 0)
{
LogProgress(context, "Error: Bill of materials not found.", Color.Red);
LogProgress(context, "Error: Bill of materials not found.", LogLevel.Error);
return;
}
LogProgress(context, $"Found {items.Count} component(s)", null);
LogProgress(context, $"Found {items.Count} component(s)");
var tempDir = CreateTempWorkDir();
// Determine drawing number
// Determine drawing number for file naming
var drawingNumber = ParseDrawingNumber(context);
if (string.IsNullOrWhiteSpace(drawingNumber))
// Export drawing to PDF
var tempDir = CreateTempWorkDir();
_drawingExporter.ExportToPdf(drawing, tempDir, context);
// Copy PDF to output folder
try
{
LogProgress(context, "Warning: Could not determine drawing number for API upload.", Color.DarkBlue);
var pdfs = Directory.GetFiles(tempDir, "*.pdf");
if (pdfs.Length > 0)
{
var savedPath = _fileExportService.SavePdfFile(pdfs[0], drawingNumber);
LogProgress(context, $"Saved PDF: {Path.GetFileName(savedPath)}", LogLevel.Info);
}
}
catch (Exception ex)
{
LogProgress(context, $"PDF save error: {ex.Message}", LogLevel.Error);
}
// Resolve drawing ID if possible
int? drawingId = null;
if (!string.IsNullOrWhiteSpace(drawingNumber))
// Create export record in database
ExportRecord exportRecord = null;
try
{
drawingId = _apiClient.ResolveDrawingIdAsync(drawingNumber).GetAwaiter().GetResult();
// Fallback: if resolve endpoint not available or failed, search equipment details
if (drawingId == null && context.EquipmentId.HasValue)
using (var db = _dbContextFactory())
{
try
db.Database.EnsureCreated();
exportRecord = new ExportRecord
{
var drawings = _apiClient.GetDrawingsForEquipmentAsync(context.EquipmentId.Value).GetAwaiter().GetResult();
if (drawings != null)
{
// Match by exact DrawingNumber (case-insensitive, trimmed)
var match = drawings.FirstOrDefault(d => string.Equals(d.DrawingNumber?.Trim(), drawingNumber.Trim(), StringComparison.OrdinalIgnoreCase));
if (match != null) drawingId = match.ID;
}
}
catch { }
DrawingNumber = drawingNumber ?? context.ActiveDocument.Title,
SourceFilePath = context.ActiveDocument.FilePath,
OutputFolder = _fileExportService.OutputFolder,
ExportedAt = DateTime.Now,
ExportedBy = System.Environment.UserName
};
db.ExportRecords.Add(exportRecord);
db.SaveChanges();
LogProgress(context, $"Created export record (ID: {exportRecord.Id})", LogLevel.Info);
}
if (drawingId == null)
{
// If equipment is provided, create the drawing on the API
if (context.EquipmentId.HasValue)
{
var create = _apiClient.CreateDrawingWithInfoAsync(context.EquipmentId.Value, drawingNumber).GetAwaiter().GetResult();
if (create != null && create.Success && create.Data.HasValue)
{
drawingId = create.Data;
LogProgress(context, "Created drawing '" + drawingNumber + "' (ID " + drawingId + ") for equipment " + context.EquipmentId, Color.Green);
}
else
{
var code = create != null ? create.StatusCode.ToString() : "?";
var err = create != null ? (create.Error ?? create.RawBody) : null;
if (!string.IsNullOrWhiteSpace(err) && err.Length > 180) err = err.Substring(0, 180) + "...";
LogProgress(context, "Warning: Could not create drawing '" + drawingNumber + "' on API (status " + code + "). " + (err ?? string.Empty), Color.DarkBlue);
}
}
else
{
LogProgress(context, $"Warning: Drawing '{drawingNumber}' not found in API; uploads will be skipped.", Color.DarkBlue);
}
}
// Export drawing to PDF first
_drawingExporter.ExportToPdf(drawing, tempDir, context);
// Upload PDF if we have a drawing number
try
{
if (!string.IsNullOrWhiteSpace(drawingNumber))
{
var pdfs = Directory.GetFiles(tempDir, "*.pdf");
var pdfName = pdfs.Length > 0 ? pdfs[0] : null;
if (pdfName != null)
{
var uploadedBy = Environment.UserName;
var ok = _apiClient.UploadDrawingPdfAsync(drawingNumber, pdfName, uploadedBy, null).GetAwaiter().GetResult();
LogProgress(context, ok ? $"Uploaded PDF for '{drawingNumber}'" : $"Failed to upload PDF for '{drawingNumber}'", ok ? Color.Green : Color.Red);
}
}
}
catch (Exception ex)
{
LogProgress(context, $"PDF upload error: {ex.Message}", Color.Red);
}
// If we still don't have an ID, resolve again after PDF upload (server may create on upload)
if (!drawingId.HasValue && !string.IsNullOrWhiteSpace(drawingNumber))
{
var resolved = _apiClient.ResolveDrawingIdAsync(drawingNumber).GetAwaiter().GetResult();
if (!resolved.HasValue && context.EquipmentId.HasValue)
{
try
{
var drawings = _apiClient.GetDrawingsForEquipmentAsync(context.EquipmentId.Value).GetAwaiter().GetResult();
if (drawings != null)
{
var match = drawings.FirstOrDefault(d => string.Equals(d.DrawingNumber?.Trim(), drawingNumber.Trim(), StringComparison.OrdinalIgnoreCase));
if (match != null) resolved = match.ID;
}
}
catch { }
}
if (resolved.HasValue)
{
drawingId = resolved;
LogProgress(context, $"Resolved drawing ID after PDF upload: {drawingId}", Color.Green);
}
}
// Then export parts to DXF and upload per-file
ExportItems(items, tempDir, context, drawingId);
// Attempt to auto-link templates at the end
try
{
if (drawingId.HasValue)
{
_apiClient.AutoLinkTemplatesAsync(drawingId.Value).GetAwaiter().GetResult();
}
}
catch { }
}
catch (Exception ex)
{
LogProgress(context, $"Database error creating export record: {ex.Message}", LogLevel.Error);
}
// Export parts to DXF (directly to output folder) and save BOM items
ExportItems(items, _fileExportService.OutputFolder, context, exportRecord?.Id);
}
#endregion
@@ -293,7 +226,7 @@ namespace ExportDXF.Services
// Set up drawing template path
context.TemplateDrawing = null;
LogProgress(context, "Export context initialized", null);
LogProgress(context, "Export context initialized");
}
private void CleanupExportContext(ExportContext context)
@@ -305,7 +238,7 @@ namespace ExportDXF.Services
}
catch (Exception ex)
{
LogProgress(context, $"Warning: Failed to cleanup template drawing: {ex.Message}", Color.DarkBlue);
LogProgress(context, $"Warning: Failed to cleanup template drawing: {ex.Message}", LogLevel.Warning);
// Don't throw - this is cleanup code
}
}
@@ -327,23 +260,22 @@ namespace ExportDXF.Services
}
catch (Exception ex)
{
LogProgress(context, $"Failed to extract items from assembly: {ex.Message}", Color.Red);
LogProgress(context, $"Failed to extract items from assembly: {ex.Message}", LogLevel.Error);
return new List<Item>();
}
}
private void ExportItems(List<Item> items, string saveDirectory, ExportContext context, int? drawingId)
private void ExportItems(List<Item> items, string saveDirectory, ExportContext context, int? exportRecordId = null)
{
LogProgress(context, "", null);
int successCount = 0;
int failureCount = 0;
int sortOrder = 0;
foreach (var item in items)
{
if (context.CancellationToken.IsCancellationRequested)
{
LogProgress(context, "Export canceled by user.", Color.DarkBlue);
LogProgress(context, "Export canceled by user.", LogLevel.Warning);
return;
}
@@ -352,54 +284,49 @@ namespace ExportDXF.Services
// PartExporter will handle template drawing creation through context
_partExporter.ExportItem(item, saveDirectory, context);
// Always create BOM item first if we have a drawing
if (drawingId.HasValue)
{
var dto = new
{
DrawingID = drawingId.Value,
ItemNo = item.ItemNo,
PartNo = !string.IsNullOrEmpty(item.FileName) ? item.FileName : item.PartName,
Qty = (int?)item.Quantity,
Description = string.IsNullOrWhiteSpace(item.Description) ? null : item.Description,
PartName = string.IsNullOrWhiteSpace(item.PartName) ? null : item.PartName,
ConfigurationName = string.IsNullOrWhiteSpace(item.Configuration) ? null : item.Configuration,
Material = string.IsNullOrWhiteSpace(item.Material) ? null : item.Material,
SortOrder = 0,
CutTemplateID = (int?)null,
FormProgramID = (int?)null
};
var bomId = _apiClient.CreateBomItemAsync(dto).GetAwaiter().GetResult();
LogProgress(context, bomId.HasValue ? $"Created BOM item for {item.ItemNo ?? item.PartName}" : $"Failed to create BOM item for {item.ItemNo ?? item.PartName}", bomId.HasValue ? Color.Green : Color.Red);
}
if (!string.IsNullOrEmpty(item.FileName))
{
successCount++;
LogProgress(context, $"Exported: {item.FileName}.dxf", LogLevel.Info);
// If we know the drawing, upload DXF
if (drawingId.HasValue)
// Create BOM item
var dxfPath = Path.Combine(saveDirectory, item.FileName + ".dxf");
var bomItem = new BomItem
{
var dxfPath = Path.Combine(saveDirectory, item.FileName + ".dxf");
if (File.Exists(dxfPath))
ExportRecordId = exportRecordId ?? 0,
ItemNo = item.ItemNo ?? "",
PartNo = item.FileName ?? item.PartName ?? "",
SortOrder = sortOrder++,
Qty = item.Quantity,
TotalQty = item.Quantity,
Description = item.Description ?? "",
PartName = item.PartName ?? "",
ConfigurationName = item.Configuration ?? "",
Material = item.Material ?? "",
Thickness = item.Thickness > 0 ? item.Thickness : null,
KFactor = item.KFactor > 0 ? item.KFactor : null,
DefaultBendRadius = item.BendRadius > 0 ? item.BendRadius : null,
DxfFilePath = dxfPath
};
// Add to UI
context.BomItemCallback?.Invoke(bomItem);
// Save BOM item to database if we have an export record
if (exportRecordId.HasValue)
{
try
{
// Zip just this file
string zipPath = CreateZipWithSingleFile(dxfPath);
try
using (var db = _dbContextFactory())
{
// Pass thickness, kfactor, default bend radius and material from the item
double? thickness = item.Thickness > 0 ? item.Thickness : (double?)null;
double? kfactor = item.KFactor > 0 ? item.KFactor : (double?)null;
double? defaultBendRadius = item.BendRadius > 0 ? item.BendRadius : (double?)null;
string material = string.IsNullOrWhiteSpace(item.Material) ? null : item.Material;
var okZip = _apiClient.UploadDxfZipAsync(drawingId.Value, zipPath, thickness, kfactor, defaultBendRadius, material).GetAwaiter().GetResult();
LogProgress(context, okZip ? $"Uploaded DXF: {Path.GetFileName(dxfPath)}" : $"Failed to upload DXF: {Path.GetFileName(dxfPath)}", okZip ? Color.Green : Color.Red);
}
finally
{
try { if (File.Exists(zipPath)) File.Delete(zipPath); } catch { }
db.BomItems.Add(bomItem);
db.SaveChanges();
}
}
catch (Exception dbEx)
{
LogProgress(context, $"Database error saving BOM item: {dbEx.Message}", LogLevel.Error);
}
}
}
else
@@ -409,20 +336,23 @@ namespace ExportDXF.Services
}
catch (Exception ex)
{
LogProgress(context, $"Error exporting item {item.ItemNo}: {ex.Message}", Color.Red);
LogProgress(context, $"Error exporting item {item.ItemNo}: {ex.Message}", LogLevel.Error);
failureCount++;
}
LogProgress(context, "", null);
}
LogProgress(context, $"Export complete: {successCount} succeeded, {failureCount} failed",
failureCount > 0 ? Color.DarkBlue : Color.Green);
failureCount > 0 ? LogLevel.Warning : LogLevel.Info);
if (exportRecordId.HasValue)
{
LogProgress(context, $"BOM items saved to database (ExportRecord ID: {exportRecordId.Value})", LogLevel.Info);
}
}
#endregion
#region Temp + Upload Helpers
#region Helper Methods
private string CreateTempWorkDir()
{
@@ -444,21 +374,6 @@ namespace ExportDXF.Services
return info != null ? ($"{info.EquipmentNo} {info.DrawingNo}") : null;
}
private string CreateZipWithSingleFile(string filePath)
{
var zipPath = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath) + ".zip");
if (File.Exists(zipPath)) File.Delete(zipPath);
using (var zip = System.IO.Compression.ZipFile.Open(zipPath, System.IO.Compression.ZipArchiveMode.Create))
{
zip.CreateEntryFromFile(filePath, Path.GetFileName(filePath));
}
return zipPath;
}
#endregion
#region Helper Methods
private void ValidateContext(ExportContext context)
{
if (context.ActiveDocument == null)
@@ -468,15 +383,11 @@ namespace ExportDXF.Services
throw new ArgumentException("ProgressCallback cannot be null.", nameof(context));
}
private void LogProgress(ExportContext context, string message, Color? color)
private void LogProgress(ExportContext context, string message, LogLevel level = LogLevel.Info, string file = null)
{
context.ProgressCallback?.Invoke(message, color);
context.ProgressCallback?.Invoke(message, level, file);
}
#endregion
}
}

View File

@@ -1,9 +1,9 @@
using ExportDXF.Extensions;
using ExportDXF.Models;
using ExportDXF.Utilities;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
using System;
using System.Drawing;
using System.IO;
namespace ExportDXF.Services
@@ -75,7 +75,7 @@ namespace ExportDXF.Services
if (item?.Component == null)
{
context.ProgressCallback?.Invoke($"Item {item?.ItemNo} - skipped, no component", Color.Yellow);
context.ProgressCallback?.Invoke("Skipped, no component", LogLevel.Warning, $"Item {item?.ItemNo}");
return;
}
@@ -88,7 +88,7 @@ namespace ExportDXF.Services
if (part == null)
{
context.ProgressCallback?.Invoke($"{item.ItemNo} - skipped, not a part document", null);
context.ProgressCallback?.Invoke("Skipped, not a part document", LogLevel.Info, item.PartName);
return;
}
@@ -150,9 +150,11 @@ namespace ExportDXF.Services
{
var model = part as ModelDoc2;
var partTitle = model.GetTitle();
if (!model.IsSheetMetal())
{
context.ProgressCallback?.Invoke($"{model.GetTitle()} - skipped, not sheet metal", null);
context.ProgressCallback?.Invoke("Skipped, not sheet metal", LogLevel.Info, partTitle);
return false;
}
@@ -164,13 +166,13 @@ namespace ExportDXF.Services
var modelName = Path.GetFileNameWithoutExtension(model.GetPathName());
sheet.SetName(modelName);
context.ProgressCallback?.Invoke($"{model.GetTitle()} - Creating flat pattern", null);
context.ProgressCallback?.Invoke("Creating flat pattern", LogLevel.Info, partTitle);
var view = CreateFlatPatternView(templateDrawing, model, configName);
if (view == null)
{
context.ProgressCallback?.Invoke($"{model.GetTitle()} - Failed to create flat pattern", Color.Red);
context.ProgressCallback?.Invoke("Failed to create flat pattern", LogLevel.Error, partTitle);
return false;
}
@@ -178,7 +180,7 @@ namespace ExportDXF.Services
if (context.ViewFlipDecider?.ShouldFlip(view) == true)
{
context.ProgressCallback?.Invoke($"{model.GetTitle()} - Flipped view", Color.Blue);
context.ProgressCallback?.Invoke("Flipped view", LogLevel.Info, partTitle);
view.FlipView = true;
}
@@ -187,7 +189,7 @@ namespace ExportDXF.Services
AddEtchLines(savePath);
context.ProgressCallback?.Invoke($"{model.GetTitle()} - Saved to \"{savePath}\"", Color.Green);
context.ProgressCallback?.Invoke($"Saved to \"{savePath}\"", LogLevel.Info, partTitle);
DeleteView(drawingModel, view);
@@ -195,7 +197,7 @@ namespace ExportDXF.Services
}
catch (Exception ex)
{
context.ProgressCallback?.Invoke($"Export failed: {ex.Message}", Color.Red);
context.ProgressCallback?.Invoke($"Export failed: {ex.Message}", LogLevel.Error, null);
return false;
}
}
@@ -232,7 +234,8 @@ namespace ExportDXF.Services
if (ViewHelper.HasSupressedBends(view))
{
context.ProgressCallback?.Invoke("A bend is suppressed, please check flat pattern", Color.Red);
var title = partModel.GetTitle();
context.ProgressCallback?.Invoke("A bend is suppressed, please check flat pattern", LogLevel.Error, title);
}
if (ViewHelper.HideModelSketches(view))
@@ -297,14 +300,16 @@ namespace ExportDXF.Services
if (desc.Contains("laser"))
{
context.ProgressCallback?.Invoke(
$"Failed to export item #{item.ItemNo} but description says it is laser cut.",
Color.Red);
"Export failed but description says it is laser cut",
LogLevel.Error,
item.PartName);
}
else if (desc.Contains("plasma"))
{
context.ProgressCallback?.Invoke(
$"Failed to export item #{item.ItemNo} but description says it is plasma cut.",
Color.Red);
"Export failed but description says it is plasma cut",
LogLevel.Error,
item.PartName);
}
}
}

View File

@@ -5,7 +5,11 @@
</startup>
<appSettings>
<add key="MaxBendRadius" value="2.0"/>
<!-- Deployed API base URL (default port from Deploy-CutFabApi.ps1) -->
<add key="CutFab.ApiBaseUrl" value="http://localhost:7027"/>
<add key="ExportOutputFolder" value="C:\ExportDXF\Output"/>
</appSettings>
<connectionStrings>
<add name="ExportDxfDb"
connectionString="Server=localhost;Database=ExportDxfDb;Trusted_Connection=True;TrustServerCertificate=True;"
providerName="Microsoft.Data.SqlClient"/>
</connectionStrings>
</configuration>