Compare commits
30 Commits
97f45b2fcc
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b1a5f0ab6 | |||
| 13009aa15e | |||
| 136a571aea | |||
| 8b1c2b5b1b | |||
| f68bddac93 | |||
| de4847b834 | |||
| cbfb9190c5 | |||
| 9b1fbd9fad | |||
| 51bf3b00dd | |||
| a32bbfa5d9 | |||
| 84f0196c97 | |||
| 5cf7e1f1e5 | |||
| 35ac0fb3f8 | |||
| cc34fb43b6 | |||
| d29d9a0e06 | |||
| c7f2a51823 | |||
| 5b996be91e | |||
| 6bddbff08e | |||
| 1ec72bc98f | |||
| b122b88435 | |||
| b677ac8ec9 | |||
| c9a8442a29 | |||
| a2b89318e1 | |||
| f1fc105a1b | |||
| 58269f9761 | |||
| 4053038632 | |||
| 2d5ffdf5c0 | |||
| 6b37f0f6f7 | |||
| c1aaaf07ee | |||
| 63b96e1451 |
23
.claude/commands/organize-commits.md
Normal file
23
.claude/commands/organize-commits.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Organize Changes into Logical Commits
|
||||
|
||||
Analyze all current git changes and organize them into logical, atomic commits. Follow these steps:
|
||||
|
||||
1. **Analyze Changes**: Run git status and git diff to see all modified and untracked files
|
||||
2. **Review Content**: Examine the actual changes in each file to understand what was modified
|
||||
3. **Group Logically**: Group changes by:
|
||||
- Feature or bug fix
|
||||
- Service or component
|
||||
- Related functionality
|
||||
- UI changes vs business logic vs API changes
|
||||
4. **Create Commits**: For each logical group:
|
||||
- Stage only the relevant files
|
||||
- Create a descriptive commit message following conventional commit format
|
||||
- Use prefixes like feat:, fix:, refactor:, chore:, docs:, etc.
|
||||
5. **Verify**: After all commits, show git log to confirm all changes were committed
|
||||
|
||||
Important guidelines:
|
||||
- Keep commits atomic (one logical change per commit)
|
||||
- Write clear, descriptive commit messages
|
||||
- Don't mix unrelated changes in the same commit
|
||||
- Follow the existing commit message style in the repository
|
||||
- Include the Claude Code attribution at the end of each commit message
|
||||
10
.claude/settings.local.json
Normal file
10
.claude/settings.local.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -242,3 +242,6 @@ ModelManifest.xml
|
||||
.fake/
|
||||
|
||||
.pfx
|
||||
|
||||
# Test documents
|
||||
TestDocs/
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "EtchBendLines"]
|
||||
path = EtchBendLines
|
||||
url = https://git.nforge.net/aj/etchbendlines.git
|
||||
url = https://git.thecozycat.net/aj/etchbendlines.git
|
||||
|
||||
Submodule EtchBendLines updated: 04031a7677...89d987f6c6
@@ -1,69 +0,0 @@
|
||||
using OfficeOpenXml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace ExportDXF
|
||||
{
|
||||
public class BomToExcel
|
||||
{
|
||||
public string TemplatePath
|
||||
{
|
||||
get { return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Templates", "BomTemplate.xlsx"); }
|
||||
}
|
||||
|
||||
public void CreateBOMExcelFile(string filepath, IList<Item> items)
|
||||
{
|
||||
File.Copy(TemplatePath, filepath, true);
|
||||
|
||||
var newFile = new FileInfo(filepath);
|
||||
|
||||
using (var pkg = new ExcelPackage(newFile))
|
||||
{
|
||||
var workbook = pkg.Workbook;
|
||||
var partsSheet = workbook.Worksheets["Parts"];
|
||||
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
var row = i + 2;
|
||||
var col = 1;
|
||||
|
||||
partsSheet.Cells[row, col++].Value = item.ItemNo;
|
||||
partsSheet.Cells[row, col++].Value = item.FileName;
|
||||
partsSheet.Cells[row, col++].Value = item.Quantity;
|
||||
partsSheet.Cells[row, col++].Value = item.Description;
|
||||
partsSheet.Cells[row, col++].Value = item.PartName;
|
||||
partsSheet.Cells[row, col++].Value = item.Configuration;
|
||||
|
||||
if (item.Thickness > 0)
|
||||
partsSheet.Cells[row, col].Value = item.Thickness;
|
||||
col++;
|
||||
|
||||
partsSheet.Cells[row, col++].Value = item.Material;
|
||||
|
||||
if (item.KFactor > 0)
|
||||
partsSheet.Cells[row, col].Value = item.KFactor;
|
||||
col++;
|
||||
|
||||
if (item.BendRadius > 0)
|
||||
partsSheet.Cells[row, col].Value = item.BendRadius;
|
||||
}
|
||||
|
||||
for (int i = 1; i <= 8; i++)
|
||||
{
|
||||
var column = partsSheet.Column(i);
|
||||
|
||||
if (column.Style.WrapText)
|
||||
continue;
|
||||
|
||||
column.AutoFit();
|
||||
column.Width += 1;
|
||||
}
|
||||
|
||||
workbook.Calculate();
|
||||
pkg.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ namespace ExportDXF
|
||||
{
|
||||
public class DrawingInfo
|
||||
{
|
||||
private static Regex drawingFormatRegex = new Regex(@"(?<jobNo>[345]\d{3}(-\d+\w{1,2})?)\s?(?<dwgNo>[ABEP]\d+(-?(\d+[A-Z]?))?)", RegexOptions.IgnoreCase);
|
||||
private static Regex drawingFormatRegex = new Regex(@"(?<equipmentNo>[345]\d{3}(-\d+\w{1,2})?)\s?(?<dwgNo>[ABEP]\d+(-?(\d+[A-Z]?))?)", RegexOptions.IgnoreCase);
|
||||
|
||||
public string JobNo { get; set; }
|
||||
public string EquipmentNo { get; set; }
|
||||
|
||||
public string DrawingNo { get; set; }
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace ExportDXF
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{JobNo} {DrawingNo}";
|
||||
return $"{EquipmentNo} {DrawingNo}";
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
@@ -39,7 +39,7 @@ namespace ExportDXF
|
||||
|
||||
var dwg = new DrawingInfo();
|
||||
|
||||
dwg.JobNo = match.Groups["jobNo"].Value;
|
||||
dwg.EquipmentNo = match.Groups["equipmentNo"].Value;
|
||||
dwg.DrawingNo = match.Groups["dwgNo"].Value;
|
||||
dwg.Source = input;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<PublisherName>Rogers Engineering</PublisherName>
|
||||
<CreateWebPageOnPublish>true</CreateWebPageOnPublish>
|
||||
<WebPage>publish.htm</WebPage>
|
||||
<ApplicationRevision>6</ApplicationRevision>
|
||||
<ApplicationRevision>8</ApplicationRevision>
|
||||
<ApplicationVersion>1.6.0.%2a</ApplicationVersion>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<PublishWizardCompleted>true</PublishWizardCompleted>
|
||||
@@ -70,6 +70,9 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="SolidWorks.Interop.sldworks, Version=24.1.0.45, Culture=neutral, PublicKeyToken=7c4797c3e4eeac03, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
@@ -85,6 +88,7 @@
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Security" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
@@ -94,30 +98,54 @@
|
||||
<Compile Include="BendOrientation.cs" />
|
||||
<Compile Include="Bounds.cs" />
|
||||
<Compile Include="DrawingInfo.cs" />
|
||||
<Compile Include="BomToExcel.cs" />
|
||||
<Compile Include="Extensions.cs" />
|
||||
<Compile Include="ItemExtractors\AssemblyItemExtractor.cs" />
|
||||
<Compile Include="ItemExtractors\BomColumnIndices.cs" />
|
||||
<Compile Include="ItemExtractors\BomItemExtractor.cs" />
|
||||
<Compile Include="ItemExtractors\ItemExtractor.cs" />
|
||||
<Compile Include="Forms\ViewFlipDeciderComboboxItem.cs" />
|
||||
<Compile Include="Item.cs" />
|
||||
<Compile Include="ViewFlipDeciders\AskViewFlipDecider.cs" />
|
||||
<Compile Include="ViewFlipDeciders\AutoViewFlipDecider.cs" />
|
||||
<Compile Include="ViewFlipDeciders\IViewFlipDecider.cs" />
|
||||
<Compile Include="Helper.cs" />
|
||||
<Compile Include="Extensions\StringExtensions.cs" />
|
||||
<Compile Include="Extensions\SolidWorksExtensions.cs" />
|
||||
<Compile Include="Extensions\TimeSpanExtensions.cs" />
|
||||
<Compile Include="Extensions\UIExtensions.cs" />
|
||||
<Compile Include="Extensions\UnitConversionExtensions.cs" />
|
||||
<Compile Include="Forms\DrawingSelectionForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Forms\DrawingSelectionForm.Designer.cs">
|
||||
<DependentUpon>DrawingSelectionForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Forms\MainForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Forms\MainForm.Designer.cs">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ItemExtractors\AssemblyItemExtractor.cs" />
|
||||
<Compile Include="ItemExtractors\BomColumnIndices.cs" />
|
||||
<Compile Include="ItemExtractors\BomItemExtractor.cs" />
|
||||
<Compile Include="ItemExtractors\ItemExtractor.cs" />
|
||||
<Compile Include="Forms\ViewFlipDeciderComboboxItem.cs" />
|
||||
<Compile Include="Models\DocumentType.cs" />
|
||||
<Compile Include="Models\ExportContext.cs" />
|
||||
<Compile Include="Models\BomItem.cs" />
|
||||
<Compile Include="Models\Item.cs" />
|
||||
<Compile Include="Models\LogEvent.cs" />
|
||||
<Compile Include="Models\SolidWorksDocument.cs" />
|
||||
<Compile Include="Services\BomExtractor.cs" />
|
||||
<Compile Include="Services\DrawingExporter.cs" />
|
||||
<Compile Include="Services\DxfExportService.cs" />
|
||||
<Compile Include="Services\CutFabApiClient.cs" />
|
||||
<Compile Include="Services\PartExporter.cs" />
|
||||
<Compile Include="Services\SolidWorksService.cs" />
|
||||
<Compile Include="Utilities\SheetMetalProperties.cs" />
|
||||
<Compile Include="Utilities\SolidWorksHelper.cs" />
|
||||
<Compile Include="Utilities\TextHelper.cs" />
|
||||
<Compile Include="Utilities\ViewHelper.cs" />
|
||||
<Compile Include="ViewFlipDeciders\AskViewFlipDecider.cs" />
|
||||
<Compile Include="ViewFlipDeciders\AutoViewFlipDecider.cs" />
|
||||
<Compile Include="ViewFlipDeciders\IViewFlipDecider.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SolidWorksExtensions.cs" />
|
||||
<Compile Include="Units.cs" />
|
||||
<Compile Include="ViewFlipDeciders\PreferUpViewFlipDecider.cs" />
|
||||
<Compile Include="ViewHelper.cs" />
|
||||
<Compile Include="ViewFlipDeciders\ViewFlipDeciderFactory.cs" />
|
||||
<EmbeddedResource Include="Forms\DrawingSelectionForm.resx">
|
||||
<DependentUpon>DrawingSelectionForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Forms\MainForm.resx">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
@@ -144,9 +172,6 @@
|
||||
<Content Include="Templates\Blank.drwdot">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Templates\BomTemplate.xlsx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\edit_alt.png" />
|
||||
@@ -174,17 +199,31 @@
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EPPlus">
|
||||
<Version>4.5.3.1</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\EtchBendLines\EtchBendLines\EtchBendLines.csproj">
|
||||
<Project>{229c2fb9-6ad6-4a5d-b83a-d1146573d6f9}</Project>
|
||||
<Name>EtchBendLines</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<COMReference Include="AcroPDFLib">
|
||||
<Guid>{05BFD3F1-6319-4F30-B752-C7A22889BCC4}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="AxAcroPDFLib">
|
||||
<Guid>{05BFD3F1-6319-4F30-B752-C7A22889BCC4}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>aximp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ExportDXF
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static void AppendText(this RichTextBox box, string text, Color color)
|
||||
{
|
||||
box.SelectionStart = box.TextLength;
|
||||
box.SelectionLength = 0;
|
||||
|
||||
box.SelectionColor = color;
|
||||
box.AppendText(text);
|
||||
box.SelectionColor = box.ForeColor;
|
||||
}
|
||||
|
||||
public static string ToReadableFormat(this TimeSpan ts)
|
||||
{
|
||||
var s = new StringBuilder();
|
||||
|
||||
if (ts.TotalHours >= 1)
|
||||
{
|
||||
var hrs = ts.Hours + ts.Days * 24.0;
|
||||
|
||||
s.Append(string.Format("{0}hrs ", hrs));
|
||||
s.Append(string.Format("{0}min ", ts.Minutes));
|
||||
s.Append(string.Format("{0}sec", ts.Seconds));
|
||||
}
|
||||
else if (ts.TotalMinutes >= 1)
|
||||
{
|
||||
s.Append(string.Format("{0}min ", ts.Minutes));
|
||||
s.Append(string.Format("{0}sec", ts.Seconds));
|
||||
}
|
||||
else
|
||||
{
|
||||
s.Append(string.Format("{0} seconds", ts.Seconds));
|
||||
}
|
||||
|
||||
return s.ToString();
|
||||
}
|
||||
|
||||
public static string PunctuateList(this IEnumerable<string> stringList)
|
||||
{
|
||||
var list = stringList.ToList();
|
||||
|
||||
switch (list.Count)
|
||||
{
|
||||
case 0:
|
||||
return string.Empty;
|
||||
|
||||
case 1:
|
||||
return list[0];
|
||||
|
||||
case 2:
|
||||
return string.Format("{0} and {1}", list[0], list[1]);
|
||||
|
||||
default:
|
||||
var s = string.Empty;
|
||||
|
||||
for (int i = 0; i < list.Count - 1; i++)
|
||||
s += list[i] + ", ";
|
||||
|
||||
s += "and " + list.Last();
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace ExportDXF
|
||||
namespace ExportDXF.Extensions
|
||||
{
|
||||
public static class SolidWorksExtensions
|
||||
{
|
||||
48
ExportDXF/Extensions/StringExtensions.cs
Normal file
48
ExportDXF/Extensions/StringExtensions.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
namespace ExportDXF.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for string manipulation.
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension method to remove XML tags from a string.
|
||||
/// </summary>
|
||||
public static string RemoveXmlTags(this string input)
|
||||
{
|
||||
return ExportDXF.Utilities.TextHelper.RemoveXmlTags(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension method to clean text (remove XML and normalize whitespace).
|
||||
/// </summary>
|
||||
public static string CleanText(this string input)
|
||||
{
|
||||
return ExportDXF.Utilities.TextHelper.CleanText(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension method to sanitize a filename.
|
||||
/// </summary>
|
||||
public static string SanitizeFileName(this string input)
|
||||
{
|
||||
return ExportDXF.Utilities.TextHelper.SanitizeFileName(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension method to truncate a string.
|
||||
/// </summary>
|
||||
public static string Truncate(this string input, int maxLength, bool useEllipsis = true)
|
||||
{
|
||||
return ExportDXF.Utilities.TextHelper.Truncate(input, maxLength, useEllipsis);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension method to convert to title case.
|
||||
/// </summary>
|
||||
public static string ToTitleCase(this string input)
|
||||
{
|
||||
return ExportDXF.Utilities.TextHelper.ToTitleCase(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
ExportDXF/Extensions/TimeSpanExtensions.cs
Normal file
31
ExportDXF/Extensions/TimeSpanExtensions.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace ExportDXF.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for TimeSpan formatting.
|
||||
/// </summary>
|
||||
public static class TimeSpanExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Formats a TimeSpan into a human-readable string.
|
||||
/// </summary>
|
||||
/// <param name="ts">The TimeSpan to format.</param>
|
||||
/// <returns>A human-readable duration string.</returns>
|
||||
public static string ToReadableFormat(this TimeSpan ts)
|
||||
{
|
||||
if (ts.TotalHours >= 1)
|
||||
{
|
||||
var totalHours = (int)(ts.TotalDays * 24 + ts.Hours);
|
||||
return $"{totalHours}hrs {ts.Minutes}min {ts.Seconds}sec";
|
||||
}
|
||||
|
||||
if (ts.TotalMinutes >= 1)
|
||||
{
|
||||
return $"{ts.Minutes}min {ts.Seconds}sec";
|
||||
}
|
||||
|
||||
return $"{ts.Seconds} seconds";
|
||||
}
|
||||
}
|
||||
}
|
||||
18
ExportDXF/Extensions/UIExtensions.cs
Normal file
18
ExportDXF/Extensions/UIExtensions.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ExportDXF.Extensions
|
||||
{
|
||||
public static class UIExtensions
|
||||
{
|
||||
public static void AppendText(this RichTextBox box, string text, Color color)
|
||||
{
|
||||
box.SelectionStart = box.TextLength;
|
||||
box.SelectionLength = 0;
|
||||
|
||||
box.SelectionColor = color;
|
||||
box.AppendText(text);
|
||||
box.SelectionColor = box.ForeColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
ExportDXF/Extensions/UnitConversionExtensions.cs
Normal file
41
ExportDXF/Extensions/UnitConversionExtensions.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
namespace ExportDXF.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for unit conversion between SolidWorks (meters) and millimeters.
|
||||
/// </summary>
|
||||
public static class UnitConversionExtensions
|
||||
{
|
||||
private const double METERS_TO_MM = 1000;
|
||||
private const double METERS_TO_INCHES = 39.37007874;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a SolidWorks dimension (in meters) to millimeters.
|
||||
/// </summary>
|
||||
/// <param name="meters">The value in meters.</param>
|
||||
/// <returns>The value in millimeters.</returns>
|
||||
public static double FromSolidWorksToMM(this double meters)
|
||||
{
|
||||
return meters * METERS_TO_MM;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts millimeters to SolidWorks dimension (meters).
|
||||
/// </summary>
|
||||
/// <param name="millimeters">The value in millimeters.</param>
|
||||
/// <returns>The value in meters.</returns>
|
||||
public static double FromMMToSolidWorks(this double millimeters)
|
||||
{
|
||||
return millimeters / METERS_TO_MM;
|
||||
}
|
||||
|
||||
public static double FromSolidWorksToInches(this double meters)
|
||||
{
|
||||
return meters * METERS_TO_INCHES;
|
||||
}
|
||||
|
||||
public static double FromInchesToSolidWorks(this double inches)
|
||||
{
|
||||
return inches / METERS_TO_INCHES;
|
||||
}
|
||||
}
|
||||
}
|
||||
283
ExportDXF/Forms/DrawingSelectionForm.Designer.cs
generated
Normal file
283
ExportDXF/Forms/DrawingSelectionForm.Designer.cs
generated
Normal file
@@ -0,0 +1,283 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
260
ExportDXF/Forms/DrawingSelectionForm.cs
Normal file
260
ExportDXF/Forms/DrawingSelectionForm.cs
Normal file
@@ -0,0 +1,260 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
120
ExportDXF/Forms/DrawingSelectionForm.resx
Normal file
120
ExportDXF/Forms/DrawingSelectionForm.resx
Normal file
@@ -0,0 +1,120 @@
|
||||
<?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>
|
||||
263
ExportDXF/Forms/MainForm.Designer.cs
generated
263
ExportDXF/Forms/MainForm.Designer.cs
generated
@@ -28,116 +28,189 @@
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.activeDocTitleBox = new System.Windows.Forms.TextBox();
|
||||
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.prefixTextBox = new System.Windows.Forms.TextBox();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
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.comboBox1 = new System.Windows.Forms.ComboBox();
|
||||
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();
|
||||
//
|
||||
// activeDocTitleBox
|
||||
// runButton
|
||||
//
|
||||
this.activeDocTitleBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.activeDocTitleBox.BackColor = System.Drawing.Color.White;
|
||||
this.activeDocTitleBox.Location = new System.Drawing.Point(130, 13);
|
||||
this.activeDocTitleBox.Name = "activeDocTitleBox";
|
||||
this.activeDocTitleBox.ReadOnly = true;
|
||||
this.activeDocTitleBox.Size = new System.Drawing.Size(584, 25);
|
||||
this.activeDocTitleBox.TabIndex = 2;
|
||||
this.activeDocTitleBox.TextChanged += new System.EventHandler(this.textBox1_TextChanged);
|
||||
//
|
||||
// richTextBox1
|
||||
//
|
||||
this.richTextBox1.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.richTextBox1.BackColor = System.Drawing.Color.White;
|
||||
this.richTextBox1.Location = new System.Drawing.Point(12, 106);
|
||||
this.richTextBox1.Name = "richTextBox1";
|
||||
this.richTextBox1.ReadOnly = true;
|
||||
this.richTextBox1.Size = new System.Drawing.Size(754, 342);
|
||||
this.richTextBox1.TabIndex = 3;
|
||||
this.richTextBox1.Text = "";
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(13, 16);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(111, 17);
|
||||
this.label1.TabIndex = 4;
|
||||
this.label1.Text = "Active document :";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(23, 47);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(101, 17);
|
||||
this.label2.TabIndex = 4;
|
||||
this.label2.Text = "Prefix files with :";
|
||||
//
|
||||
// prefixTextBox
|
||||
//
|
||||
this.prefixTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.prefixTextBox.Location = new System.Drawing.Point(130, 44);
|
||||
this.prefixTextBox.Name = "prefixTextBox";
|
||||
this.prefixTextBox.Size = new System.Drawing.Size(584, 25);
|
||||
this.prefixTextBox.TabIndex = 2;
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button1.Image = global::ExportDXF.Properties.Resources.play;
|
||||
this.button1.Location = new System.Drawing.Point(720, 13);
|
||||
this.button1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(46, 56);
|
||||
this.button1.TabIndex = 0;
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
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);
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(12, 78);
|
||||
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 = 4;
|
||||
this.label3.TabIndex = 2;
|
||||
this.label3.Text = "View flip decider :";
|
||||
//
|
||||
// comboBox1
|
||||
// viewFlipDeciderBox
|
||||
//
|
||||
this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.comboBox1.FormattingEnabled = true;
|
||||
this.comboBox1.Location = new System.Drawing.Point(130, 75);
|
||||
this.comboBox1.Name = "comboBox1";
|
||||
this.comboBox1.Size = new System.Drawing.Size(353, 25);
|
||||
this.comboBox1.TabIndex = 5;
|
||||
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;
|
||||
//
|
||||
// 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;
|
||||
//
|
||||
// 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;
|
||||
//
|
||||
// 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;
|
||||
//
|
||||
// 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;
|
||||
//
|
||||
// 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;
|
||||
//
|
||||
// cutTemplatesTab
|
||||
//
|
||||
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;
|
||||
//
|
||||
// cutTemplatesDataGrid
|
||||
//
|
||||
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;
|
||||
//
|
||||
// dwgDetailsTab
|
||||
//
|
||||
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;
|
||||
//
|
||||
// drawingPdfViewer
|
||||
//
|
||||
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;
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
|
||||
this.ClientSize = new System.Drawing.Size(778, 460);
|
||||
this.Controls.Add(this.comboBox1);
|
||||
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.label2);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.richTextBox1);
|
||||
this.Controls.Add(this.prefixTextBox);
|
||||
this.Controls.Add(this.activeDocTitleBox);
|
||||
this.Controls.Add(this.button1);
|
||||
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();
|
||||
|
||||
@@ -145,14 +218,18 @@
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.TextBox activeDocTitleBox;
|
||||
private System.Windows.Forms.RichTextBox richTextBox1;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.TextBox prefixTextBox;
|
||||
private System.Windows.Forms.Button runButton;
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.ComboBox comboBox1;
|
||||
private System.Windows.Forms.ComboBox viewFlipDeciderBox;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -117,4 +117,12 @@
|
||||
<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>
|
||||
@@ -1,35 +0,0 @@
|
||||
using SolidWorks.Interop.sldworks;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ExportDXF
|
||||
{
|
||||
public static class Helper
|
||||
{
|
||||
public static string GetNumWithSuffix(int i)
|
||||
{
|
||||
if (i >= 11 && i <= 13)
|
||||
return i.ToString() + "th";
|
||||
|
||||
var j = i % 10;
|
||||
|
||||
switch (j)
|
||||
{
|
||||
case 1: return i.ToString() + "st";
|
||||
case 2: return i.ToString() + "nd";
|
||||
case 3: return i.ToString() + "rd";
|
||||
default: return i.ToString() + "th";
|
||||
}
|
||||
}
|
||||
|
||||
public static void UncheckFlatPatternCornerTreatment(ModelDoc2 model)
|
||||
{
|
||||
var flatPattern = model.GetFeatureByTypeName("FlatPattern");
|
||||
var flatPatternFeatureData = flatPattern.GetDefinition() as FlatPatternFeatureData;
|
||||
|
||||
flatPatternFeatureData.CornerTreatment = false;
|
||||
flatPatternFeatureData.SimplifyBends = true;
|
||||
|
||||
var ret = flatPattern.ModifyDefinition(flatPatternFeatureData, model, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using SolidWorks.Interop.sldworks;
|
||||
|
||||
namespace ExportDXF
|
||||
{
|
||||
public class Item
|
||||
{
|
||||
public string ItemNo { get; set; }
|
||||
|
||||
public string FileName { get; set; }
|
||||
|
||||
public string PartName { get; set; }
|
||||
|
||||
public string Configuration { get; set; }
|
||||
|
||||
public int Quantity { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
|
||||
public double Thickness { get; set; }
|
||||
|
||||
public double KFactor { get; set; }
|
||||
|
||||
public double BendRadius { get; set; }
|
||||
|
||||
public string Material { get; set; }
|
||||
|
||||
public Component2 Component { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using SolidWorks.Interop.sldworks;
|
||||
using ExportDXF.Extensions;
|
||||
using ExportDXF.Services;
|
||||
using SolidWorks.Interop.sldworks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using SolidWorks.Interop.sldworks;
|
||||
using ExportDXF.Extensions;
|
||||
using ExportDXF.Services;
|
||||
using ExportDXF.Utilities;
|
||||
using SolidWorks.Interop.sldworks;
|
||||
using SolidWorks.Interop.swconst;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -51,22 +54,42 @@ namespace ExportDXF.ItemExtractors
|
||||
|
||||
if (columnIndices.ItemNumber != -1)
|
||||
{
|
||||
item.ItemNo = table.DisplayedText[rowIndex, columnIndices.ItemNumber];
|
||||
var x = table.DisplayedText[rowIndex, columnIndices.ItemNumber];
|
||||
x = TextHelper.RemoveXmlTags(x);
|
||||
|
||||
double d;
|
||||
|
||||
if (double.TryParse(x, out d))
|
||||
{
|
||||
item.ItemNo = d.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
item.ItemNo = x;
|
||||
}
|
||||
}
|
||||
|
||||
if (columnIndices.PartNumber != -1)
|
||||
{
|
||||
item.PartName = table.DisplayedText[rowIndex, columnIndices.PartNumber];
|
||||
var x = table.DisplayedText[rowIndex, columnIndices.PartNumber];
|
||||
x = TextHelper.RemoveXmlTags(x);
|
||||
|
||||
item.PartName = x;
|
||||
|
||||
}
|
||||
|
||||
if (columnIndices.Description != -1)
|
||||
{
|
||||
item.Description = table.DisplayedText[rowIndex, columnIndices.Description];
|
||||
var x = table.DisplayedText[rowIndex, columnIndices.Description];
|
||||
x = TextHelper.RemoveXmlTags(x);
|
||||
|
||||
item.Description = x;
|
||||
}
|
||||
|
||||
if (columnIndices.Quantity != -1)
|
||||
{
|
||||
var qtyString = table.DisplayedText[rowIndex, columnIndices.Quantity];
|
||||
qtyString = TextHelper.RemoveXmlTags(qtyString);
|
||||
|
||||
int qty = 0;
|
||||
int.TryParse(qtyString, out qty);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using ExportDXF.Services;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ExportDXF.ItemExtractors
|
||||
{
|
||||
|
||||
32
ExportDXF/Models/BomItem.cs
Normal file
32
ExportDXF/Models/BomItem.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace ExportDXF.Models
|
||||
{
|
||||
public class BomItem
|
||||
{
|
||||
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; } = "";
|
||||
|
||||
// Sheet metal properties from CutTemplate
|
||||
public double? Thickness { get; set; }
|
||||
public double? KFactor { get; set; }
|
||||
public double? DefaultBendRadius { get; set; }
|
||||
}
|
||||
|
||||
public struct Size
|
||||
{
|
||||
public double Width { get; set; }
|
||||
public double Height { get; set; }
|
||||
}
|
||||
}
|
||||
13
ExportDXF/Models/DocumentType.cs
Normal file
13
ExportDXF/Models/DocumentType.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace ExportDXF.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of SolidWorks document types.
|
||||
/// </summary>
|
||||
public enum DocumentType
|
||||
{
|
||||
Unknown,
|
||||
Part,
|
||||
Assembly,
|
||||
Drawing
|
||||
}
|
||||
}
|
||||
135
ExportDXF/Models/ExportContext.cs
Normal file
135
ExportDXF/Models/ExportContext.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
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;
|
||||
|
||||
namespace ExportDXF.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Context object containing all information needed for an export operation.
|
||||
/// </summary>
|
||||
public class ExportContext
|
||||
{
|
||||
private const string DRAWING_TEMPLATE_FOLDER = "Templates";
|
||||
private const string DRAWING_TEMPLATE_FILE = "Blank.drwdot";
|
||||
|
||||
/// <summary>
|
||||
/// The document to be exported.
|
||||
/// </summary>
|
||||
public SolidWorksDocument ActiveDocument { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The view flip decider to determine if views should be flipped.
|
||||
/// </summary>
|
||||
public IViewFlipDecider ViewFlipDecider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Prefix to prepend to exported filenames.
|
||||
/// </summary>
|
||||
public string FilePrefix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Selected Equipment ID for API operations (optional).
|
||||
/// </summary>
|
||||
public int? EquipmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cancellation token for canceling the export operation.
|
||||
/// </summary>
|
||||
public CancellationToken CancellationToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Callback for reporting progress and status messages.
|
||||
/// </summary>
|
||||
public Action<string, Color?> ProgressCallback { get; set; }
|
||||
|
||||
public void LogProgress(string message, Color? color = null)
|
||||
{
|
||||
ProgressCallback?.Invoke(message, color);
|
||||
}
|
||||
|
||||
public SldWorks SolidWorksApp { get; set; }
|
||||
|
||||
public DrawingDoc TemplateDrawing { get; set; }
|
||||
|
||||
private string DrawingTemplatePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(
|
||||
Application.StartupPath,
|
||||
DRAWING_TEMPLATE_FOLDER,
|
||||
DRAWING_TEMPLATE_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
public DrawingDoc GetOrCreateTemplateDrawing()
|
||||
{
|
||||
if (TemplateDrawing != null)
|
||||
return TemplateDrawing;
|
||||
|
||||
TemplateDrawing = SolidWorksApp.NewDocument(
|
||||
DrawingTemplatePath,
|
||||
(int)swDwgPaperSizes_e.swDwgPaperDsize,
|
||||
1,
|
||||
1) as DrawingDoc;
|
||||
|
||||
return TemplateDrawing;
|
||||
}
|
||||
|
||||
public void CleanupTemplateDrawing()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (TemplateDrawing == null)
|
||||
return;
|
||||
|
||||
if (SolidWorksApp == null)
|
||||
{
|
||||
ProgressCallback?.Invoke("Warning: Cannot cleanup template drawing - SolidWorks app not available", Color.DarkBlue);
|
||||
TemplateDrawing = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var model = TemplateDrawing as ModelDoc2;
|
||||
if (model != null)
|
||||
{
|
||||
var title = model.GetTitle();
|
||||
|
||||
if (!string.IsNullOrEmpty(title))
|
||||
{
|
||||
// Close the document without saving
|
||||
SolidWorksApp.CloseDoc(title);
|
||||
ProgressCallback?.Invoke("Closed template drawing", null);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the reference regardless of success/failure
|
||||
TemplateDrawing = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ProgressCallback?.Invoke($"Failed to close template drawing: {ex.Message}", Color.Red);
|
||||
|
||||
// Still clear the reference to prevent further issues
|
||||
TemplateDrawing = null;
|
||||
|
||||
// Don't throw here as this is cleanup code - log the error but continue
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseDocument(string title)
|
||||
{
|
||||
SolidWorksApp?.CloseDoc(title);
|
||||
}
|
||||
|
||||
public ModelDoc2 CreateDocument(string templatePath, int paperSize, double width, double height)
|
||||
{
|
||||
return SolidWorksApp?.NewDocument(templatePath, paperSize, width, height) as ModelDoc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
ExportDXF/Models/Item.cs
Normal file
65
ExportDXF/Models/Item.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using SolidWorks.Interop.sldworks;
|
||||
|
||||
namespace ExportDXF.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an item extracted from a BOM or assembly.
|
||||
/// </summary>
|
||||
public class Item
|
||||
{
|
||||
/// <summary>
|
||||
/// Item number from the BOM.
|
||||
/// </summary>
|
||||
public string ItemNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Part name or file name.
|
||||
/// </summary>
|
||||
public string PartName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configuration name.
|
||||
/// </summary>
|
||||
public string Configuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Item description.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Quantity of this item.
|
||||
/// </summary>
|
||||
public int Quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Material specification.
|
||||
/// </summary>
|
||||
public string Material { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sheet metal thickness in millimeters.
|
||||
/// </summary>
|
||||
public double Thickness { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sheet metal K-factor.
|
||||
/// </summary>
|
||||
public double KFactor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Bend radius in millimeters.
|
||||
/// </summary>
|
||||
public double BendRadius { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The exported DXF filename (without path or extension).
|
||||
/// </summary>
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The SolidWorks component reference.
|
||||
/// </summary>
|
||||
public Component2 Component { get; set; }
|
||||
}
|
||||
}
|
||||
22
ExportDXF/Models/LogEvent.cs
Normal file
22
ExportDXF/Models/LogEvent.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace ExportDXF.Models
|
||||
{
|
||||
public enum LogLevel { Info, Warning, Error }
|
||||
|
||||
public enum LogAction { Start, FindBom, CreateFlat, FlipView, SavePdf, UploadPdf, UploadDxf, CreateBomItem }
|
||||
|
||||
public sealed class LogEvent
|
||||
{
|
||||
public DateTime Time { get; set; } = DateTime.Now;
|
||||
public LogLevel Level { get; set; }
|
||||
public string Equipment { get; set; } = "";
|
||||
public string Drawing { get; set; } = "";
|
||||
public string Part { get; set; } = "";
|
||||
public LogAction Action { get; set; }
|
||||
public string Target { get; set; } = "";
|
||||
public string Result { get; set; } = "OK";
|
||||
public int DurationMs { get; set; }
|
||||
public string Message { get; set; } = "";
|
||||
}
|
||||
}
|
||||
30
ExportDXF/Models/SolidWorksDocument.cs
Normal file
30
ExportDXF/Models/SolidWorksDocument.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using ExportDXF.Models;
|
||||
|
||||
namespace ExportDXF.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a SolidWorks document with essential metadata.
|
||||
/// </summary>
|
||||
public class SolidWorksDocument
|
||||
{
|
||||
/// <summary>
|
||||
/// The title/name of the document.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The full file path of the document.
|
||||
/// </summary>
|
||||
public string FilePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of document (Part, Assembly, or Drawing).
|
||||
/// </summary>
|
||||
public DocumentType DocumentType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The native SolidWorks document object (ModelDoc2, PartDoc, etc.).
|
||||
/// </summary>
|
||||
public object NativeDocument { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,95 @@
|
||||
using System;
|
||||
using ExportDXF.Forms;
|
||||
using ExportDXF.Services;
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ExportDXF
|
||||
{
|
||||
internal static class Program
|
||||
static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
private static void Main()
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new Forms.MainForm());
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
|
||||
public ServiceContainer()
|
||||
{
|
||||
_baseUrl = ConfigurationManager.AppSettings["CutFab.ApiBaseUrl"] ?? "http://localhost:7027";
|
||||
_apiClient = new CutFabApiClient(_baseUrl);
|
||||
}
|
||||
|
||||
public async Task<DrawingSelectionForm> ResolveDrawingSelectionAsync()
|
||||
{
|
||||
// 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 exportService = new DxfExportService(
|
||||
solidWorksService,
|
||||
bomExtractor,
|
||||
partExporter,
|
||||
drawingExporter,
|
||||
_apiClient);
|
||||
|
||||
return new MainForm(solidWorksService, exportService, _apiClient, selectedDrawingId, selectedDrawingNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
66
ExportDXF/Services/BomExtractor.cs
Normal file
66
ExportDXF/Services/BomExtractor.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using ExportDXF.Extensions;
|
||||
using ExportDXF.ItemExtractors;
|
||||
using SolidWorks.Interop.sldworks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace ExportDXF.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for extracting items from a Bill of Materials (BOM).
|
||||
/// </summary>
|
||||
public interface IBomExtractor
|
||||
{
|
||||
/// <summary>
|
||||
/// 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>
|
||||
/// <returns>A list of extracted items.</returns>
|
||||
List<Item> ExtractFromDrawing(DrawingDoc drawing, Action<string, Color?> progressCallback);
|
||||
}
|
||||
|
||||
public class BomExtractor : IBomExtractor
|
||||
{
|
||||
public List<Item> ExtractFromDrawing(DrawingDoc drawing, Action<string, Color?> progressCallback)
|
||||
{
|
||||
if (drawing == null)
|
||||
throw new ArgumentNullException(nameof(drawing));
|
||||
|
||||
var bomTables = drawing.GetBomTables();
|
||||
|
||||
if (bomTables.Count == 0)
|
||||
{
|
||||
progressCallback?.Invoke("Error: Bill of materials not found.", Color.Red);
|
||||
return new List<Item>();
|
||||
}
|
||||
|
||||
progressCallback?.Invoke($"Found {bomTables.Count} BOM table(s)", null);
|
||||
|
||||
var allItems = new List<Item>();
|
||||
|
||||
foreach (var bom in bomTables)
|
||||
{
|
||||
try
|
||||
{
|
||||
var extractor = new BomItemExtractor(bom)
|
||||
{
|
||||
SkipHiddenRows = true
|
||||
};
|
||||
|
||||
progressCallback?.Invoke($"Fetching components from {bom.BomFeature.Name}", null);
|
||||
|
||||
var items = extractor.GetItems();
|
||||
allItems.AddRange(items);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
progressCallback?.Invoke($"Failed to extract from {bom.BomFeature.Name}: {ex.Message}", Color.Red);
|
||||
}
|
||||
}
|
||||
|
||||
return allItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
468
ExportDXF/Services/CutFabApiClient.cs
Normal file
468
ExportDXF/Services/CutFabApiClient.cs
Normal file
@@ -0,0 +1,468 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
115
ExportDXF/Services/DrawingExporter.cs
Normal file
115
ExportDXF/Services/DrawingExporter.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using SolidWorks.Interop.sldworks;
|
||||
using SolidWorks.Interop.swconst;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
|
||||
namespace ExportDXF.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for exporting drawing documents to PDF format.
|
||||
/// </summary>
|
||||
public interface IDrawingExporter
|
||||
{
|
||||
/// <summary>
|
||||
/// Exports a drawing document to PDF format.
|
||||
/// </summary>
|
||||
/// <param name="drawing">The drawing document to export.</param>
|
||||
/// <param name="saveDirectory">The directory where the PDF file will be saved.</param>
|
||||
/// <param name="context">The export context containing SolidWorks app and callbacks.</param>
|
||||
void ExportToPdf(DrawingDoc drawing, string saveDirectory, ExportContext context);
|
||||
}
|
||||
|
||||
public class DrawingExporter : IDrawingExporter
|
||||
{
|
||||
public void ExportToPdf(DrawingDoc drawing, string saveDirectory, ExportContext context)
|
||||
{
|
||||
if (drawing == null)
|
||||
throw new ArgumentNullException(nameof(drawing));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(saveDirectory))
|
||||
throw new ArgumentException("Save directory cannot be null or empty.", nameof(saveDirectory));
|
||||
|
||||
if (context == null)
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
|
||||
if (context.SolidWorksApp == null)
|
||||
throw new ArgumentException("SolidWorksApp cannot be null in context.", nameof(context));
|
||||
|
||||
try
|
||||
{
|
||||
var pdfFileName = GetPdfFileName(drawing);
|
||||
var pdfPath = Path.Combine(saveDirectory, pdfFileName);
|
||||
|
||||
var model = drawing as ModelDoc2;
|
||||
var sldWorks = context.SolidWorksApp;
|
||||
|
||||
var exportData = sldWorks.GetExportFileData(
|
||||
(int)swExportDataFileType_e.swExportPdfData) as ExportPdfData;
|
||||
|
||||
if (exportData == null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to get PDF export data from SolidWorks.");
|
||||
}
|
||||
|
||||
exportData.ViewPdfAfterSaving = false;
|
||||
exportData.SetSheets(
|
||||
(int)swExportDataSheetsToExport_e.swExportData_ExportAllSheets,
|
||||
drawing);
|
||||
|
||||
context.ProgressCallback?.Invoke($"Exporting drawing to PDF: \"{pdfFileName}\"", null);
|
||||
|
||||
int errors = 0;
|
||||
int warnings = 0;
|
||||
|
||||
var modelExtension = model.Extension;
|
||||
var success = modelExtension.SaveAs(
|
||||
pdfPath,
|
||||
(int)swSaveAsVersion_e.swSaveAsCurrentVersion,
|
||||
(int)swSaveAsOptions_e.swSaveAsOptions_Silent,
|
||||
exportData,
|
||||
ref errors,
|
||||
ref warnings);
|
||||
|
||||
if (success && errors == 0)
|
||||
{
|
||||
context.ProgressCallback?.Invoke($"Saved drawing to PDF: \"{pdfFileName}\"", Color.Green);
|
||||
}
|
||||
else if (success && warnings > 0)
|
||||
{
|
||||
context.ProgressCallback?.Invoke(
|
||||
$"PDF export completed with warnings: {warnings}",
|
||||
Color.DarkBlue);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.ProgressCallback?.Invoke(
|
||||
$"PDF export failed. Errors: {errors}, Warnings: {warnings}",
|
||||
Color.Red);
|
||||
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);
|
||||
throw new InvalidOperationException(errorMessage, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetPdfFileName(DrawingDoc drawing)
|
||||
{
|
||||
var model = drawing as ModelDoc2;
|
||||
var modelFilePath = model.GetPathName();
|
||||
|
||||
if (string.IsNullOrEmpty(modelFilePath))
|
||||
{
|
||||
// Handle unsaved documents
|
||||
var title = model.GetTitle();
|
||||
return string.IsNullOrEmpty(title) ? "Untitled.pdf" : Path.GetFileNameWithoutExtension(title) + ".pdf";
|
||||
}
|
||||
|
||||
return Path.GetFileNameWithoutExtension(modelFilePath) + ".pdf";
|
||||
}
|
||||
}
|
||||
}
|
||||
482
ExportDXF/Services/DxfExportService.cs
Normal file
482
ExportDXF/Services/DxfExportService.cs
Normal file
@@ -0,0 +1,482 @@
|
||||
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
|
||||
{
|
||||
public interface IDxfExportService
|
||||
{
|
||||
/// <summary>
|
||||
/// Exports the document specified in the context to DXF format.
|
||||
/// </summary>
|
||||
/// <param name="context">The export context containing all necessary information.</param>
|
||||
void Export(ExportContext context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service responsible for orchestrating the export of SolidWorks documents to DXF format.
|
||||
/// </summary>
|
||||
public class DxfExportService : IDxfExportService
|
||||
{
|
||||
private readonly ISolidWorksService _solidWorksService;
|
||||
private readonly IBomExtractor _bomExtractor;
|
||||
private readonly IPartExporter _partExporter;
|
||||
private readonly IDrawingExporter _drawingExporter;
|
||||
private readonly ICutFabApiClient _apiClient;
|
||||
|
||||
public DxfExportService(
|
||||
ISolidWorksService solidWorksService,
|
||||
IBomExtractor bomExtractor,
|
||||
IPartExporter partExporter,
|
||||
IDrawingExporter drawingExporter,
|
||||
ICutFabApiClient apiClient)
|
||||
{
|
||||
_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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports the document specified in the context to DXF format.
|
||||
/// </summary>
|
||||
public void Export(ExportContext context)
|
||||
{
|
||||
if (context == null)
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
|
||||
ValidateContext(context);
|
||||
SetupExportContext(context);
|
||||
|
||||
var startTime = DateTime.Now;
|
||||
|
||||
try
|
||||
{
|
||||
_solidWorksService.EnableUserControl(false);
|
||||
|
||||
switch (context.ActiveDocument.DocumentType)
|
||||
{
|
||||
case DocumentType.Part:
|
||||
ExportPart(context);
|
||||
break;
|
||||
|
||||
case DocumentType.Assembly:
|
||||
ExportAssembly(context);
|
||||
break;
|
||||
|
||||
case DocumentType.Drawing:
|
||||
ExportDrawing(context);
|
||||
break;
|
||||
|
||||
default:
|
||||
LogProgress(context, "Unknown document type.", Color.Red);
|
||||
break;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanupExportContext(context);
|
||||
_solidWorksService.EnableUserControl(true);
|
||||
|
||||
var duration = DateTime.Now - startTime;
|
||||
LogProgress(context, $"Run time: {duration.ToReadableFormat()}", null);
|
||||
}
|
||||
}
|
||||
|
||||
#region Export Methods by Document Type
|
||||
|
||||
private void ExportPart(ExportContext context)
|
||||
{
|
||||
LogProgress(context, "Active document is a Part", null);
|
||||
|
||||
var part = context.ActiveDocument.NativeDocument as PartDoc;
|
||||
if (part == null)
|
||||
{
|
||||
LogProgress(context, "Failed to get part document.", Color.Red);
|
||||
return;
|
||||
}
|
||||
|
||||
var tempDir = CreateTempWorkDir();
|
||||
_partExporter.ExportSinglePart(part, tempDir, context);
|
||||
}
|
||||
|
||||
private void ExportAssembly(ExportContext context)
|
||||
{
|
||||
LogProgress(context, "Active document is an Assembly", null);
|
||||
LogProgress(context, "Fetching components...", null);
|
||||
|
||||
var assembly = context.ActiveDocument.NativeDocument as AssemblyDoc;
|
||||
if (assembly == null)
|
||||
{
|
||||
LogProgress(context, "Failed to get assembly document.", Color.Red);
|
||||
return;
|
||||
}
|
||||
|
||||
var items = ExtractItemsFromAssembly(assembly, context);
|
||||
|
||||
if (items == null || items.Count == 0)
|
||||
{
|
||||
LogProgress(context, "No items found in assembly.", Color.DarkBlue);
|
||||
return;
|
||||
}
|
||||
|
||||
LogProgress(context, $"Found {items.Count} item(s).", null);
|
||||
|
||||
var tempDir = CreateTempWorkDir();
|
||||
ExportItems(items, tempDir, context, drawingId: null);
|
||||
}
|
||||
|
||||
private void ExportDrawing(ExportContext context)
|
||||
{
|
||||
LogProgress(context, "Active document is a Drawing", null);
|
||||
LogProgress(context, "Finding BOM tables...", null);
|
||||
|
||||
var drawing = context.ActiveDocument.NativeDocument as DrawingDoc;
|
||||
if (drawing == null)
|
||||
{
|
||||
LogProgress(context, "Failed to get drawing document.", Color.Red);
|
||||
return;
|
||||
}
|
||||
|
||||
var items = _bomExtractor.ExtractFromDrawing(drawing, context.ProgressCallback);
|
||||
|
||||
if (items == null || items.Count == 0)
|
||||
{
|
||||
LogProgress(context, "Error: Bill of materials not found.", Color.Red);
|
||||
return;
|
||||
}
|
||||
|
||||
LogProgress(context, $"Found {items.Count} component(s)", null);
|
||||
|
||||
var tempDir = CreateTempWorkDir();
|
||||
|
||||
// Determine drawing number
|
||||
var drawingNumber = ParseDrawingNumber(context);
|
||||
if (string.IsNullOrWhiteSpace(drawingNumber))
|
||||
{
|
||||
LogProgress(context, "Warning: Could not determine drawing number for API upload.", Color.DarkBlue);
|
||||
}
|
||||
|
||||
// Resolve drawing ID if possible
|
||||
int? drawingId = null;
|
||||
if (!string.IsNullOrWhiteSpace(drawingNumber))
|
||||
{
|
||||
drawingId = _apiClient.ResolveDrawingIdAsync(drawingNumber).GetAwaiter().GetResult();
|
||||
// Fallback: if resolve endpoint not available or failed, search equipment details
|
||||
if (drawingId == null && context.EquipmentId.HasValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
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 { }
|
||||
}
|
||||
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 { }
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Context Management
|
||||
|
||||
private void SetupExportContext(ExportContext context)
|
||||
{
|
||||
// Set up SolidWorks application reference
|
||||
context.SolidWorksApp = _solidWorksService.GetNativeSldWorks();
|
||||
|
||||
if (context.SolidWorksApp == null)
|
||||
{
|
||||
throw new InvalidOperationException("SolidWorks service is not connected.");
|
||||
}
|
||||
|
||||
// Set up drawing template path
|
||||
context.TemplateDrawing = null;
|
||||
|
||||
LogProgress(context, "Export context initialized", null);
|
||||
}
|
||||
|
||||
private void CleanupExportContext(ExportContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Clean up template drawing if it was created
|
||||
context.CleanupTemplateDrawing();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogProgress(context, $"Warning: Failed to cleanup template drawing: {ex.Message}", Color.DarkBlue);
|
||||
// Don't throw - this is cleanup code
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Item Processing
|
||||
|
||||
private List<Item> ExtractItemsFromAssembly(AssemblyDoc assembly, ExportContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var extractor = new AssemblyItemExtractor(assembly)
|
||||
{
|
||||
TopLevelOnly = false
|
||||
};
|
||||
|
||||
return extractor.GetItems();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogProgress(context, $"Failed to extract items from assembly: {ex.Message}", Color.Red);
|
||||
return new List<Item>();
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportItems(List<Item> items, string saveDirectory, ExportContext context, int? drawingId)
|
||||
{
|
||||
LogProgress(context, "", null);
|
||||
|
||||
int successCount = 0;
|
||||
int failureCount = 0;
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (context.CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
LogProgress(context, "Export canceled by user.", Color.DarkBlue);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 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++;
|
||||
|
||||
// If we know the drawing, upload DXF
|
||||
if (drawingId.HasValue)
|
||||
{
|
||||
var dxfPath = Path.Combine(saveDirectory, item.FileName + ".dxf");
|
||||
if (File.Exists(dxfPath))
|
||||
{
|
||||
// Zip just this file
|
||||
string zipPath = CreateZipWithSingleFile(dxfPath);
|
||||
try
|
||||
{
|
||||
// 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 { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
failureCount++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogProgress(context, $"Error exporting item {item.ItemNo}: {ex.Message}", Color.Red);
|
||||
failureCount++;
|
||||
}
|
||||
|
||||
LogProgress(context, "", null);
|
||||
}
|
||||
|
||||
LogProgress(context, $"Export complete: {successCount} succeeded, {failureCount} failed",
|
||||
failureCount > 0 ? Color.DarkBlue : Color.Green);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Temp + Upload Helpers
|
||||
|
||||
private string CreateTempWorkDir()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), "ExportDXF-" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
private string ParseDrawingNumber(ExportContext context)
|
||||
{
|
||||
// Prefer prefix (e.g., "5007 A02 PT"), fallback to active document title
|
||||
var candidate = context?.FilePrefix;
|
||||
var info = string.IsNullOrWhiteSpace(candidate) ? null : DrawingInfo.Parse(candidate);
|
||||
if (info == null)
|
||||
{
|
||||
var title = context?.ActiveDocument?.Title;
|
||||
info = string.IsNullOrWhiteSpace(title) ? null : DrawingInfo.Parse(title);
|
||||
}
|
||||
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)
|
||||
throw new ArgumentException("ActiveDocument cannot be null.", nameof(context));
|
||||
|
||||
if (context.ProgressCallback == null)
|
||||
throw new ArgumentException("ProgressCallback cannot be null.", nameof(context));
|
||||
}
|
||||
|
||||
private void LogProgress(ExportContext context, string message, Color? color)
|
||||
{
|
||||
context.ProgressCallback?.Invoke(message, color);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
311
ExportDXF/Services/PartExporter.cs
Normal file
311
ExportDXF/Services/PartExporter.cs
Normal file
@@ -0,0 +1,311 @@
|
||||
using ExportDXF.Extensions;
|
||||
using ExportDXF.Utilities;
|
||||
using SolidWorks.Interop.sldworks;
|
||||
using SolidWorks.Interop.swconst;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
|
||||
namespace ExportDXF.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for exporting parts to DXF format.
|
||||
/// </summary>
|
||||
public interface IPartExporter
|
||||
{
|
||||
/// <summary>
|
||||
/// Exports a single part document to DXF.
|
||||
/// </summary>
|
||||
/// <param name="part">The part document to export.</param>
|
||||
/// <param name="saveDirectory">The directory where the DXF file will be saved.</param>
|
||||
/// <param name="context">The export context.</param>
|
||||
void ExportSinglePart(PartDoc part, string saveDirectory, ExportContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Exports an item (component from BOM or assembly) to DXF.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to export.</param>
|
||||
/// <param name="saveDirectory">The directory where the DXF file will be saved.</param>
|
||||
/// <param name="context">The export context.</param>
|
||||
void ExportItem(Item item, string saveDirectory, ExportContext context);
|
||||
}
|
||||
|
||||
public class PartExporter : IPartExporter
|
||||
{
|
||||
public void ExportSinglePart(PartDoc part, string saveDirectory, ExportContext context)
|
||||
{
|
||||
if (part == null)
|
||||
throw new ArgumentNullException(nameof(part));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(saveDirectory))
|
||||
throw new ArgumentException("Save directory cannot be null or empty.", nameof(saveDirectory));
|
||||
|
||||
if (context == null)
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
|
||||
var model = part as ModelDoc2;
|
||||
var activeConfig = model.GetActiveConfiguration() as SolidWorks.Interop.sldworks.Configuration;
|
||||
var originalConfigName = activeConfig?.Name;
|
||||
|
||||
try
|
||||
{
|
||||
var fileName = GetSinglePartFileName(model, context.FilePrefix);
|
||||
var savePath = Path.Combine(saveDirectory, fileName + ".dxf");
|
||||
|
||||
context.GetOrCreateTemplateDrawing();
|
||||
|
||||
ExportPartToDxf(part, originalConfigName, savePath, context);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (originalConfigName != null)
|
||||
{
|
||||
model.ShowConfiguration(originalConfigName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ExportItem(Item item, string saveDirectory, ExportContext context)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(saveDirectory))
|
||||
throw new ArgumentException("Save directory cannot be null or empty.", nameof(saveDirectory));
|
||||
|
||||
if (context == null)
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
|
||||
if (item?.Component == null)
|
||||
{
|
||||
context.ProgressCallback?.Invoke($"Item {item?.ItemNo} - skipped, no component", Color.Yellow);
|
||||
return;
|
||||
}
|
||||
|
||||
context.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
item.Component.SetLightweightToResolved();
|
||||
|
||||
var model = item.Component.GetModelDoc2() as ModelDoc2;
|
||||
var part = model as PartDoc;
|
||||
|
||||
if (part == null)
|
||||
{
|
||||
context.ProgressCallback?.Invoke($"{item.ItemNo} - skipped, not a part document", null);
|
||||
return;
|
||||
}
|
||||
|
||||
EnrichItemWithMetadata(item, model, part);
|
||||
|
||||
var fileName = GetItemFileName(item, context.FilePrefix);
|
||||
var savePath = Path.Combine(saveDirectory, fileName + ".dxf");
|
||||
|
||||
var templateDrawing = context.GetOrCreateTemplateDrawing();
|
||||
|
||||
if (ExportPartToDxf(part, item.Component.ReferencedConfiguration, savePath, context))
|
||||
{
|
||||
item.FileName = Path.GetFileNameWithoutExtension(savePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogExportFailure(item, context);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnrichItemWithMetadata(Item item, ModelDoc2 model, PartDoc part)
|
||||
{
|
||||
// Get sheet metal properties
|
||||
var sheetMetalProps = SolidWorksHelper.GetSheetMetalProperties(model);
|
||||
if (sheetMetalProps != null)
|
||||
{
|
||||
item.Thickness = sheetMetalProps.Thickness;
|
||||
item.KFactor = sheetMetalProps.KFactor;
|
||||
item.BendRadius = sheetMetalProps.BendRadius;
|
||||
}
|
||||
|
||||
// Get description from custom properties
|
||||
var config = item.Component.ReferencedConfiguration;
|
||||
|
||||
// Try configuration-specific properties first
|
||||
var configPropertyManager = model.Extension.CustomPropertyManager[config];
|
||||
item.Description = configPropertyManager?.Get("Description");
|
||||
|
||||
// Fall back to document-level properties if no config-specific description
|
||||
if (string.IsNullOrEmpty(item.Description))
|
||||
{
|
||||
var docPropertyManager = model.Extension.CustomPropertyManager[""];
|
||||
item.Description = docPropertyManager?.Get("Description");
|
||||
}
|
||||
|
||||
item.Description = TextHelper.RemoveXmlTags(item.Description);
|
||||
|
||||
// Get material
|
||||
item.Material = part.GetMaterialPropertyName2(config, out _);
|
||||
}
|
||||
|
||||
private bool ExportPartToDxf(
|
||||
PartDoc part,
|
||||
string configName,
|
||||
string savePath,
|
||||
ExportContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = part as ModelDoc2;
|
||||
|
||||
if (!model.IsSheetMetal())
|
||||
{
|
||||
context.ProgressCallback?.Invoke($"{model.GetTitle()} - skipped, not sheet metal", null);
|
||||
return false;
|
||||
}
|
||||
|
||||
var templateDrawing = context.GetOrCreateTemplateDrawing();
|
||||
|
||||
SolidWorksHelper.ConfigureFlatPatternSettings(model);
|
||||
|
||||
var sheet = templateDrawing.IGetCurrentSheet();
|
||||
var modelName = Path.GetFileNameWithoutExtension(model.GetPathName());
|
||||
sheet.SetName(modelName);
|
||||
|
||||
context.ProgressCallback?.Invoke($"{model.GetTitle()} - Creating flat pattern", null);
|
||||
|
||||
var view = CreateFlatPatternView(templateDrawing, model, configName);
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
context.ProgressCallback?.Invoke($"{model.GetTitle()} - Failed to create flat pattern", Color.Red);
|
||||
return false;
|
||||
}
|
||||
|
||||
ConfigureFlatPatternView(view, templateDrawing, model, configName, context);
|
||||
|
||||
if (context.ViewFlipDecider?.ShouldFlip(view) == true)
|
||||
{
|
||||
context.ProgressCallback?.Invoke($"{model.GetTitle()} - Flipped view", Color.Blue);
|
||||
view.FlipView = true;
|
||||
}
|
||||
|
||||
var drawingModel = templateDrawing as ModelDoc2;
|
||||
drawingModel.SaveAs(savePath);
|
||||
|
||||
AddEtchLines(savePath);
|
||||
|
||||
context.ProgressCallback?.Invoke($"{model.GetTitle()} - Saved to \"{savePath}\"", Color.Green);
|
||||
|
||||
DeleteView(drawingModel, view);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.ProgressCallback?.Invoke($"Export failed: {ex.Message}", Color.Red);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private SolidWorks.Interop.sldworks.View CreateFlatPatternView(
|
||||
DrawingDoc drawing,
|
||||
ModelDoc2 part,
|
||||
string configName)
|
||||
{
|
||||
return drawing.CreateFlatPatternViewFromModelView3(
|
||||
part.GetPathName(),
|
||||
configName,
|
||||
0, 0, 0,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
private void ConfigureFlatPatternView(
|
||||
SolidWorks.Interop.sldworks.View view,
|
||||
DrawingDoc drawing,
|
||||
ModelDoc2 partModel,
|
||||
string configName,
|
||||
ExportContext context)
|
||||
{
|
||||
view.ShowSheetMetalBendNotes = true;
|
||||
|
||||
var drawingModel = drawing as ModelDoc2;
|
||||
drawingModel.ViewZoomtofit2();
|
||||
|
||||
var flatPatternModel = ViewHelper.GetModelFromView(view);
|
||||
SolidWorksHelper.SetFlatPatternSuppressionState(
|
||||
flatPatternModel,
|
||||
swComponentSuppressionState_e.swComponentFullyResolved);
|
||||
|
||||
if (ViewHelper.HasSupressedBends(view))
|
||||
{
|
||||
context.ProgressCallback?.Invoke("A bend is suppressed, please check flat pattern", Color.Red);
|
||||
}
|
||||
|
||||
if (ViewHelper.HideModelSketches(view))
|
||||
{
|
||||
// Recreate view without sketches
|
||||
DeleteView(drawingModel, view);
|
||||
|
||||
view = CreateFlatPatternView(drawing, partModel, configName);
|
||||
view.ShowSheetMetalBendNotes = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteView(ModelDoc2 drawing, SolidWorks.Interop.sldworks.View view)
|
||||
{
|
||||
drawing.SelectByName(0, view.Name);
|
||||
drawing.DeleteSelection(false);
|
||||
}
|
||||
|
||||
private void AddEtchLines(string dxfPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var etcher = new EtchBendLines.Etcher();
|
||||
etcher.AddEtchLines(dxfPath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Silently fail if etch lines can't be added
|
||||
}
|
||||
}
|
||||
|
||||
private string GetSinglePartFileName(ModelDoc2 model, string prefix)
|
||||
{
|
||||
var title = model.GetTitle().Replace(".SLDPRT", "");
|
||||
var config = model.ConfigurationManager.ActiveConfiguration.Name;
|
||||
var isDefaultConfig = string.Equals(config, "default", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var name = isDefaultConfig ? title : $"{title} [{config}]";
|
||||
return prefix + name;
|
||||
}
|
||||
|
||||
private string GetItemFileName(Item item, string prefix)
|
||||
{
|
||||
prefix = prefix?.Replace("\"", "''") ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(item.ItemNo))
|
||||
{
|
||||
return prefix + item.PartName;
|
||||
}
|
||||
|
||||
var num = item.ItemNo.PadLeft(2, '0');
|
||||
// Expected format: {DrawingNo} PT{ItemNo}
|
||||
return string.IsNullOrWhiteSpace(prefix)
|
||||
? $"PT{num}"
|
||||
: $"{prefix} PT{num}";
|
||||
}
|
||||
|
||||
private void LogExportFailure(Item item, ExportContext context)
|
||||
{
|
||||
var desc = item.Description?.ToLower() ?? string.Empty;
|
||||
|
||||
if (desc.Contains("laser"))
|
||||
{
|
||||
context.ProgressCallback?.Invoke(
|
||||
$"Failed to export item #{item.ItemNo} but description says it is laser cut.",
|
||||
Color.Red);
|
||||
}
|
||||
else if (desc.Contains("plasma"))
|
||||
{
|
||||
context.ProgressCallback?.Invoke(
|
||||
$"Failed to export item #{item.ItemNo} but description says it is plasma cut.",
|
||||
Color.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
406
ExportDXF/Services/SolidWorksService.cs
Normal file
406
ExportDXF/Services/SolidWorksService.cs
Normal file
@@ -0,0 +1,406 @@
|
||||
using ExportDXF.Models;
|
||||
using SolidWorks.Interop.sldworks;
|
||||
using SolidWorks.Interop.swconst;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ExportDXF.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for managing SolidWorks application connection and document lifecycle.
|
||||
/// </summary>
|
||||
public interface ISolidWorksService : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Connects to the SolidWorks application asynchronously.
|
||||
/// </summary>
|
||||
Task ConnectAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently active SolidWorks document.
|
||||
/// </summary>
|
||||
/// <returns>The active document or null if no document is open.</returns>
|
||||
SolidWorksDocument GetActiveDocument();
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables user control of the SolidWorks application.
|
||||
/// </summary>
|
||||
/// <param name="enable">True to enable user control, false to disable.</param>
|
||||
void EnableUserControl(bool enable);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SolidWorks application instance.
|
||||
/// </summary>
|
||||
/// <returns>The SldWorks instance.</returns>
|
||||
SldWorks GetNativeSldWorks();
|
||||
|
||||
/// <summary>
|
||||
/// Closes the document with the specified title.
|
||||
/// </summary>
|
||||
/// <param name="documentTitle"></param>
|
||||
/// <returns></returns>
|
||||
bool CloseDocument(string documentTitle);
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when the active document changes.
|
||||
/// </summary>
|
||||
event EventHandler ActiveDocumentChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service for managing SolidWorks application connection and document lifecycle.
|
||||
/// </summary>
|
||||
public class SolidWorksService : ISolidWorksService
|
||||
{
|
||||
private SldWorks _sldWorks;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when the active document changes in SolidWorks.
|
||||
/// </summary>
|
||||
public event EventHandler ActiveDocumentChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Connects to the SolidWorks application asynchronously.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when connection to SolidWorks fails.</exception>
|
||||
public async Task ConnectAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to get running instance first
|
||||
_sldWorks = GetRunningInstance();
|
||||
|
||||
// If no running instance, create new one
|
||||
if (_sldWorks == null)
|
||||
{
|
||||
_sldWorks = CreateNewInstance();
|
||||
}
|
||||
|
||||
if (_sldWorks == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Failed to connect to SolidWorks. Please ensure SolidWorks is installed.");
|
||||
}
|
||||
|
||||
// Make SolidWorks visible
|
||||
_sldWorks.Visible = true;
|
||||
|
||||
// Subscribe to document change events
|
||||
_sldWorks.ActiveModelDocChangeNotify += OnSolidWorksActiveDocChanged;
|
||||
}
|
||||
catch (COMException ex)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"COM error while connecting to SolidWorks: {ex.Message}", ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently active SolidWorks document.
|
||||
/// </summary>
|
||||
/// <returns>The active document wrapper, or null if no document is open.</returns>
|
||||
public SolidWorksDocument GetActiveDocument()
|
||||
{
|
||||
if (_sldWorks == null)
|
||||
return null;
|
||||
|
||||
var model = _sldWorks.ActiveDoc as ModelDoc2;
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return CreateDocumentWrapper(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables user control of the SolidWorks application.
|
||||
/// When disabled, SolidWorks won't show dialogs or allow user interaction.
|
||||
/// </summary>
|
||||
/// <param name="enable">True to enable user control, false to disable.</param>
|
||||
public void EnableUserControl(bool enable)
|
||||
{
|
||||
if (_sldWorks != null)
|
||||
{
|
||||
_sldWorks.UserControl = enable;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the native SolidWorks application instance.
|
||||
/// Use this when you need direct access to the SolidWorks API.
|
||||
/// </summary>
|
||||
/// <returns>The SldWorks instance, or null if not connected.</returns>
|
||||
public SldWorks GetNativeSldWorks()
|
||||
{
|
||||
return _sldWorks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if SolidWorks is connected and ready.
|
||||
/// </summary>
|
||||
public bool IsConnected => _sldWorks != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version of the connected SolidWorks instance.
|
||||
/// </summary>
|
||||
public string GetSolidWorksVersion()
|
||||
{
|
||||
if (_sldWorks == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return _sldWorks.RevisionNumber();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes a document by its title.
|
||||
/// </summary>
|
||||
/// <param name="documentTitle">The title of the document to close.</param>
|
||||
/// <returns>True if successfully closed, false otherwise.</returns>
|
||||
public bool CloseDocument(string documentTitle)
|
||||
{
|
||||
if (_sldWorks == null || string.IsNullOrWhiteSpace(documentTitle))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
_sldWorks.CloseDoc(documentTitle);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new document from a template.
|
||||
/// </summary>
|
||||
/// <param name="templatePath">Path to the template file.</param>
|
||||
/// <param name="paperSize">Paper size for drawings.</param>
|
||||
/// <param name="width">Width dimension.</param>
|
||||
/// <param name="height">Height dimension.</param>
|
||||
/// <returns>The created document, or null if creation failed.</returns>
|
||||
public ModelDoc2 CreateDocument(string templatePath, int paperSize, double width, double height)
|
||||
{
|
||||
if (_sldWorks == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return _sldWorks.NewDocument(templatePath, paperSize, width, height) as ModelDoc2;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens an existing document.
|
||||
/// </summary>
|
||||
/// <param name="filePath">Full path to the document.</param>
|
||||
/// <param name="options">Open options.</param>
|
||||
/// <param name="configuration">Configuration to open (empty for default).</param>
|
||||
/// <returns>The opened document, or null if open failed.</returns>
|
||||
public ModelDoc2 OpenDocument(string filePath, int options = 0, string configuration = "")
|
||||
{
|
||||
if (_sldWorks == null || string.IsNullOrWhiteSpace(filePath))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
int errors = 0;
|
||||
int warnings = 0;
|
||||
|
||||
var doc = _sldWorks.OpenDoc6(
|
||||
filePath,
|
||||
(int)GetDocumentType(filePath),
|
||||
options,
|
||||
configuration,
|
||||
ref errors,
|
||||
ref warnings);
|
||||
|
||||
return doc as ModelDoc2;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private SldWorks GetRunningInstance()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Marshal.GetActiveObject("SldWorks.Application") as SldWorks;
|
||||
}
|
||||
catch (COMException)
|
||||
{
|
||||
// No running instance
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private SldWorks CreateNewInstance()
|
||||
{
|
||||
try
|
||||
{
|
||||
var type = Type.GetTypeFromProgID("SldWorks.Application");
|
||||
if (type == null)
|
||||
return null;
|
||||
|
||||
return Activator.CreateInstance(type) as SldWorks;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private SolidWorksDocument CreateDocumentWrapper(ModelDoc2 model)
|
||||
{
|
||||
return new SolidWorksDocument
|
||||
{
|
||||
Title = model.GetTitle(),
|
||||
FilePath = model.GetPathName(),
|
||||
DocumentType = DetermineDocumentType(model),
|
||||
NativeDocument = model
|
||||
};
|
||||
}
|
||||
|
||||
private DocumentType DetermineDocumentType(ModelDoc2 model)
|
||||
{
|
||||
if (model is PartDoc) return DocumentType.Part;
|
||||
if (model is AssemblyDoc) return DocumentType.Assembly;
|
||||
if (model is DrawingDoc) return DocumentType.Drawing;
|
||||
return DocumentType.Unknown;
|
||||
}
|
||||
|
||||
private swDocumentTypes_e GetDocumentType(string filePath)
|
||||
{
|
||||
var extension = System.IO.Path.GetExtension(filePath)?.ToLowerInvariant();
|
||||
|
||||
switch (extension)
|
||||
{
|
||||
case ".sldprt":
|
||||
return swDocumentTypes_e.swDocPART;
|
||||
case ".sldasm":
|
||||
return swDocumentTypes_e.swDocASSEMBLY;
|
||||
case ".slddrw":
|
||||
return swDocumentTypes_e.swDocDRAWING;
|
||||
default:
|
||||
return swDocumentTypes_e.swDocNONE;
|
||||
}
|
||||
}
|
||||
|
||||
private int OnSolidWorksActiveDocChanged()
|
||||
{
|
||||
try
|
||||
{
|
||||
ActiveDocumentChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Swallow exceptions from event handlers
|
||||
}
|
||||
|
||||
return 1; // SolidWorks expects 1 to be returned
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Releases all resources used by the SolidWorksService.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and optionally managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">True to release both managed and unmanaged resources;
|
||||
/// false to release only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
// Unsubscribe from events
|
||||
if (_sldWorks != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_sldWorks.ActiveModelDocChangeNotify -= OnSolidWorksActiveDocChanged;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Release COM object
|
||||
if (_sldWorks != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Marshal.ReleaseComObject(_sldWorks);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
|
||||
_sldWorks = null;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public SldWorks GetSldWorks()
|
||||
{
|
||||
if (_sldWorks == null)
|
||||
{
|
||||
_sldWorks = GetRunningInstance();
|
||||
return _sldWorks;
|
||||
}
|
||||
else
|
||||
{
|
||||
_sldWorks = CreateNewInstance();
|
||||
return _sldWorks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizer to ensure COM objects are released.
|
||||
/// </summary>
|
||||
~SolidWorksService()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ExportDXF
|
||||
{
|
||||
public static class Units
|
||||
{
|
||||
/// <summary>
|
||||
/// Multiply factor needed to convert the desired units to meters.
|
||||
/// </summary>
|
||||
public static double ScaleFactor = 0.0254; // inches to meters
|
||||
|
||||
public static double ToSldWorks(this double d)
|
||||
{
|
||||
return Math.Round(d * ScaleFactor, 8);
|
||||
}
|
||||
|
||||
public static double FromSldWorks(this double d)
|
||||
{
|
||||
return Math.Round(d / ScaleFactor, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
ExportDXF/Utilities/SheetMetalProperties.cs
Normal file
43
ExportDXF/Utilities/SheetMetalProperties.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
namespace ExportDXF.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains sheet metal properties extracted from a SolidWorks part.
|
||||
/// </summary>
|
||||
public class SheetMetalProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// Material thickness
|
||||
/// </summary>
|
||||
public double Thickness { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// K-factor for bend calculations.
|
||||
/// </summary>
|
||||
public double KFactor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Inside bend radius
|
||||
/// </summary>
|
||||
public double BendRadius { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Bend allowance
|
||||
/// </summary>
|
||||
public double BendAllowance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether auto relief is enabled.
|
||||
/// </summary>
|
||||
public bool AutoRelief { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Relief ratio for auto relief.
|
||||
/// </summary>
|
||||
public double ReliefRatio { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Thickness: {Thickness:F2}\", K-Factor: {KFactor:F3}, Bend Radius: {BendRadius:F2}\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
513
ExportDXF/Utilities/SolidWorksHelper.cs
Normal file
513
ExportDXF/Utilities/SolidWorksHelper.cs
Normal file
@@ -0,0 +1,513 @@
|
||||
using ExportDXF.Extensions;
|
||||
using SolidWorks.Interop.sldworks;
|
||||
using SolidWorks.Interop.swconst;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ExportDXF.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class for SolidWorks-specific operations and helper functions.
|
||||
/// </summary>
|
||||
public static class SolidWorksHelper
|
||||
{
|
||||
#region Feature Names Constants
|
||||
|
||||
private const string FLAT_PATTERN_FEATURE = "FlatPattern";
|
||||
private const string SHEET_METAL_FEATURE = "SheetMetal";
|
||||
private const string BASE_FLANGE_FEATURE = "BaseFlange";
|
||||
private const string FLAT_PATTERN_FOLDER = "Flat-Pattern";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Sheet Metal Operations
|
||||
|
||||
/// <summary>
|
||||
/// Gets sheet metal properties from the model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to extract properties from.</param>
|
||||
/// <returns>Sheet metal properties, or null if not a sheet metal part.</returns>
|
||||
public static SheetMetalProperties GetSheetMetalProperties(ModelDoc2 model)
|
||||
{
|
||||
if (model == null)
|
||||
throw new ArgumentNullException(nameof(model));
|
||||
|
||||
var sheetMetalFeature = model.GetFeatureByTypeName(SHEET_METAL_FEATURE);
|
||||
|
||||
if (sheetMetalFeature == null)
|
||||
return null;
|
||||
|
||||
var sheetMetalData = sheetMetalFeature.GetDefinition() as SheetMetalFeatureData;
|
||||
|
||||
if (sheetMetalData == null)
|
||||
return null;
|
||||
|
||||
return new SheetMetalProperties
|
||||
{
|
||||
Thickness = sheetMetalData.Thickness.FromSolidWorksToInches(),
|
||||
KFactor = sheetMetalData.KFactor,
|
||||
BendRadius = sheetMetalData.BendRadius.FromSolidWorksToInches(),
|
||||
BendAllowance = sheetMetalData.BendAllowance.FromSolidWorksToInches(),
|
||||
AutoRelief = sheetMetalData.UseAutoRelief,
|
||||
ReliefRatio = sheetMetalData.ReliefRatio
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures flat pattern settings for optimal DXF export.
|
||||
/// Unchecks corner treatment and enables simplify bends.
|
||||
/// </summary>
|
||||
/// <param name="model">The model containing the flat pattern.</param>
|
||||
/// <returns>True if settings were successfully modified.</returns>
|
||||
public static bool ConfigureFlatPatternSettings(ModelDoc2 model)
|
||||
{
|
||||
if (model == null)
|
||||
throw new ArgumentNullException(nameof(model));
|
||||
|
||||
var flatPattern = model.GetFeatureByTypeName(FLAT_PATTERN_FEATURE);
|
||||
|
||||
if (flatPattern == null)
|
||||
return false;
|
||||
|
||||
var featureData = flatPattern.GetDefinition() as FlatPatternFeatureData;
|
||||
|
||||
if (featureData == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
// Configure for cleaner DXF output
|
||||
featureData.CornerTreatment = false; // Remove corner treatments
|
||||
featureData.SimplifyBends = true; // Simplify bend representations
|
||||
|
||||
return flatPattern.ModifyDefinition(featureData, model, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the suppression state of the flat pattern feature.
|
||||
/// </summary>
|
||||
/// <param name="model">The model containing the flat pattern.</param>
|
||||
/// <param name="suppressionState">The desired suppression state.</param>
|
||||
/// <returns>True if the flat pattern is suppressed after the operation.</returns>
|
||||
public static bool SetFlatPatternSuppressionState(
|
||||
ModelDoc2 model,
|
||||
swComponentSuppressionState_e suppressionState)
|
||||
{
|
||||
if (model == null)
|
||||
throw new ArgumentNullException(nameof(model));
|
||||
|
||||
var flatPattern = model.GetFeatureByTypeName(FLAT_PATTERN_FEATURE);
|
||||
|
||||
if (flatPattern == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
flatPattern.SetSuppression((int)suppressionState);
|
||||
return flatPattern.IsSuppressed();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flat pattern feature from a model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to search.</param>
|
||||
/// <returns>The flat pattern feature, or null if not found.</returns>
|
||||
public static Feature GetFlatPatternFeature(ModelDoc2 model)
|
||||
{
|
||||
return model?.GetFeatureByTypeName(FLAT_PATTERN_FEATURE);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a model has a flat pattern feature.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to check.</param>
|
||||
/// <returns>True if the model has a flat pattern feature.</returns>
|
||||
public static bool HasFlatPattern(ModelDoc2 model)
|
||||
{
|
||||
return GetFlatPatternFeature(model) != null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Feature Operations
|
||||
|
||||
/// <summary>
|
||||
/// Gets all features of a specific type from a model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to search.</param>
|
||||
/// <param name="featureTypeName">The name of the feature type to find.</param>
|
||||
/// <returns>A list of features of the specified type.</returns>
|
||||
public static List<Feature> GetFeaturesByTypeName(ModelDoc2 model, string featureTypeName)
|
||||
{
|
||||
if (model == null || string.IsNullOrEmpty(featureTypeName))
|
||||
return new List<Feature>();
|
||||
|
||||
var features = new List<Feature>();
|
||||
var feature = model.FirstFeature() as Feature;
|
||||
|
||||
while (feature != null)
|
||||
{
|
||||
if (string.Equals(feature.GetTypeName2(), featureTypeName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
features.Add(feature);
|
||||
}
|
||||
|
||||
// Check sub-features
|
||||
var subFeature = feature.GetFirstSubFeature() as Feature;
|
||||
while (subFeature != null)
|
||||
{
|
||||
if (string.Equals(subFeature.GetTypeName2(), featureTypeName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
features.Add(subFeature);
|
||||
}
|
||||
subFeature = subFeature.GetNextSubFeature() as Feature;
|
||||
}
|
||||
|
||||
feature = feature.GetNextFeature() as Feature;
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Suppresses or unsuppresses a feature by name.
|
||||
/// </summary>
|
||||
/// <param name="model">The model containing the feature.</param>
|
||||
/// <param name="featureName">The name of the feature to suppress/unsuppress.</param>
|
||||
/// <param name="suppress">True to suppress, false to unsuppress.</param>
|
||||
/// <returns>True if the operation was successful.</returns>
|
||||
public static bool SetFeatureSuppressionByName(ModelDoc2 model, string featureName, bool suppress)
|
||||
{
|
||||
if (model == null || string.IsNullOrEmpty(featureName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
// Use Extension.SelectByID2 to find and select the feature
|
||||
var modelExtension = model.Extension;
|
||||
bool selected = modelExtension.SelectByID2(featureName, "BODYFEATURE", 0, 0, 0, false, 0, null, 0);
|
||||
|
||||
if (!selected)
|
||||
return false;
|
||||
|
||||
var selectionManager = model.SelectionManager as SelectionMgr;
|
||||
var feature = selectionManager.GetSelectedObject6(1, -1) as Feature;
|
||||
|
||||
if (feature == null)
|
||||
return false;
|
||||
|
||||
var suppressionState = suppress
|
||||
? swFeatureSuppressionAction_e.swSuppressFeature
|
||||
: swFeatureSuppressionAction_e.swUnSuppressFeature;
|
||||
|
||||
feature.SetSuppression((int)suppressionState);
|
||||
|
||||
// Clear the selection
|
||||
model.ClearSelection2(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Configuration Operations
|
||||
|
||||
/// <summary>
|
||||
/// Gets all configuration names from a model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to query.</param>
|
||||
/// <returns>A list of configuration names.</returns>
|
||||
public static List<string> GetConfigurationNames(ModelDoc2 model)
|
||||
{
|
||||
if (model == null)
|
||||
return new List<string>();
|
||||
|
||||
var configNames = (string[])model.GetConfigurationNames();
|
||||
return configNames?.ToList() ?? new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates a specific configuration.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to modify.</param>
|
||||
/// <param name="configurationName">The name of the configuration to activate.</param>
|
||||
/// <returns>True if the configuration was successfully activated.</returns>
|
||||
public static bool ActivateConfiguration(ModelDoc2 model, string configurationName)
|
||||
{
|
||||
if (model == null || string.IsNullOrEmpty(configurationName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
return model.ShowConfiguration2(configurationName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active configuration name.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to query.</param>
|
||||
/// <returns>The name of the active configuration, or null if unavailable.</returns>
|
||||
public static string GetActiveConfigurationName(ModelDoc2 model)
|
||||
{
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var config = model.GetActiveConfiguration() as Configuration;
|
||||
return config?.Name;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Custom Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets a custom property value from a model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to query.</param>
|
||||
/// <param name="propertyName">The name of the property.</param>
|
||||
/// <param name="configurationName">The configuration name (empty string for file-level properties).</param>
|
||||
/// <returns>The property value, or null if not found.</returns>
|
||||
public static string GetCustomProperty(ModelDoc2 model, string propertyName, string configurationName = "")
|
||||
{
|
||||
if (model == null || string.IsNullOrEmpty(propertyName))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var customPropertyManager = model.Extension.CustomPropertyManager[configurationName ?? ""];
|
||||
return customPropertyManager.Get(propertyName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a custom property value on a model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to modify.</param>
|
||||
/// <param name="propertyName">The name of the property.</param>
|
||||
/// <param name="propertyValue">The value to set.</param>
|
||||
/// <param name="configurationName">The configuration name (empty string for file-level properties).</param>
|
||||
/// <returns>True if the property was successfully set.</returns>
|
||||
public static bool SetCustomProperty(ModelDoc2 model, string propertyName, string propertyValue, string configurationName = "")
|
||||
{
|
||||
if (model == null || string.IsNullOrEmpty(propertyName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var customPropertyManager = model.Extension.CustomPropertyManager[configurationName ?? ""];
|
||||
var result = customPropertyManager.Add3(propertyName, (int)swCustomInfoType_e.swCustomInfoText, propertyValue, (int)swCustomPropertyAddOption_e.swCustomPropertyReplaceValue);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all custom property names from a model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to query.</param>
|
||||
/// <param name="configurationName">The configuration name (empty string for file-level properties).</param>
|
||||
/// <returns>A list of custom property names.</returns>
|
||||
public static List<string> GetCustomPropertyNames(ModelDoc2 model, string configurationName = "")
|
||||
{
|
||||
if (model == null)
|
||||
return new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
var customPropertyManager = model.Extension.CustomPropertyManager[configurationName ?? ""];
|
||||
var propertyNames = (string[])customPropertyManager.GetNames();
|
||||
return propertyNames?.ToList() ?? new List<string>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Material Operations
|
||||
|
||||
/// <summary>
|
||||
/// Gets the material name for a part document.
|
||||
/// </summary>
|
||||
/// <param name="part">The part document.</param>
|
||||
/// <param name="configurationName">The configuration name.</param>
|
||||
/// <returns>The material name, or null if not assigned.</returns>
|
||||
public static string GetMaterial(PartDoc part, string configurationName)
|
||||
{
|
||||
if (part == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return part.GetMaterialPropertyName2(configurationName, out _);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the material for a part document.
|
||||
/// </summary>
|
||||
/// <param name="part">The part document.</param>
|
||||
/// <param name="materialName">The name of the material to assign.</param>
|
||||
/// <param name="databasePath">The path to the material database.</param>
|
||||
/// <returns>True if the material was successfully assigned.</returns>
|
||||
public static bool SetMaterial(PartDoc part, string materialName, string databasePath)
|
||||
{
|
||||
if (part == null || string.IsNullOrEmpty(materialName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
part.SetMaterialPropertyName2("", databasePath, materialName);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Document Operations
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file path of a model document.
|
||||
/// </summary>
|
||||
/// <param name="model">The model document.</param>
|
||||
/// <returns>The full file path, or empty string if not saved.</returns>
|
||||
public static string GetFilePath(ModelDoc2 model)
|
||||
{
|
||||
return model?.GetPathName() ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the document title (filename without path).
|
||||
/// </summary>
|
||||
/// <param name="model">The model document.</param>
|
||||
/// <returns>The document title.</returns>
|
||||
public static string GetTitle(ModelDoc2 model)
|
||||
{
|
||||
return model?.GetTitle() ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a document has been saved.
|
||||
/// </summary>
|
||||
/// <param name="model">The model document.</param>
|
||||
/// <returns>True if the document has been saved to disk.</returns>
|
||||
public static bool IsSaved(ModelDoc2 model)
|
||||
{
|
||||
return !string.IsNullOrEmpty(GetFilePath(model));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a document.
|
||||
/// </summary>
|
||||
/// <param name="model">The model document to save.</param>
|
||||
/// <returns>True if the save operation was successful.</returns>
|
||||
public static bool Save(ModelDoc2 model)
|
||||
{
|
||||
if (model == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
int errors = 0;
|
||||
int warnings = 0;
|
||||
return model.Save3((int)swSaveAsOptions_e.swSaveAsOptions_Silent, ref errors, ref warnings);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Component Operations
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a lightweight component to fully loaded.
|
||||
/// </summary>
|
||||
/// <param name="component">The component to resolve.</param>
|
||||
/// <returns>True if the component was successfully resolved.</returns>
|
||||
public static bool ResolveComponent(Component2 component)
|
||||
{
|
||||
if (component == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
component.SetLightweightToResolved();
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a component is suppressed.
|
||||
/// </summary>
|
||||
/// <param name="component">The component to check.</param>
|
||||
/// <returns>True if the component is suppressed.</returns>
|
||||
public static bool IsComponentSuppressed(Component2 component)
|
||||
{
|
||||
return component?.IsSuppressed() ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the model document from a component.
|
||||
/// </summary>
|
||||
/// <param name="component">The component.</param>
|
||||
/// <returns>The model document, or null if unavailable.</returns>
|
||||
public static ModelDoc2 GetModelFromComponent(Component2 component)
|
||||
{
|
||||
return component?.GetModelDoc2() as ModelDoc2;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
274
ExportDXF/Utilities/TextHelper.cs
Normal file
274
ExportDXF/Utilities/TextHelper.cs
Normal file
@@ -0,0 +1,274 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ExportDXF.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class for text processing and string manipulation operations.
|
||||
/// </summary>
|
||||
public static class TextHelper
|
||||
{
|
||||
private static readonly Regex XmlTagRegex = new Regex(@"<[^>]+>", RegexOptions.Compiled);
|
||||
private static readonly Regex WhitespaceRegex = new Regex(@"\s+", RegexOptions.Compiled);
|
||||
private static readonly Regex FontTagRegex = new Regex(@"<FONT.*?>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all XML tags from the input string.
|
||||
/// </summary>
|
||||
/// <param name="input">The string containing XML tags to remove.</param>
|
||||
/// <returns>The string with all XML tags removed, or the original input if null/empty.</returns>
|
||||
public static string RemoveXmlTags(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return input;
|
||||
|
||||
return XmlTagRegex.Replace(input, string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes specific SolidWorks font XML tags from the input string.
|
||||
/// This is more targeted than RemoveXmlTags and handles SolidWorks-specific formatting.
|
||||
/// </summary>
|
||||
/// <param name="input">The string containing font tags to remove.</param>
|
||||
/// <returns>The string with font tags removed.</returns>
|
||||
public static string RemoveFontXmlTags(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return input;
|
||||
|
||||
var result = input;
|
||||
var matches = FontTagRegex.Matches(result);
|
||||
|
||||
// Process matches in reverse order to maintain indices
|
||||
for (int i = matches.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var match = matches[i];
|
||||
result = result.Remove(match.Index, match.Length);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes whitespace in a string by replacing multiple consecutive whitespace characters with a single space.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to normalize.</param>
|
||||
/// <returns>The string with normalized whitespace.</returns>
|
||||
public static string NormalizeWhitespace(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return input;
|
||||
|
||||
return WhitespaceRegex.Replace(input.Trim(), " ");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans text by removing XML tags and normalizing whitespace.
|
||||
/// This is a common operation for processing text from SolidWorks.
|
||||
/// </summary>
|
||||
/// <param name="input">The text to clean.</param>
|
||||
/// <returns>Cleaned text with XML tags removed and whitespace normalized.</returns>
|
||||
public static string CleanText(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return input;
|
||||
|
||||
var cleaned = RemoveXmlTags(input);
|
||||
return NormalizeWhitespace(cleaned);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a number with its ordinal suffix (1st, 2nd, 3rd, 4th, etc.).
|
||||
/// </summary>
|
||||
/// <param name="number">The number to format.</param>
|
||||
/// <returns>The number with appropriate ordinal suffix.</returns>
|
||||
public static string GetOrdinalSuffix(int number)
|
||||
{
|
||||
if (number <= 0)
|
||||
return number.ToString();
|
||||
|
||||
// Special cases for 11th, 12th, 13th
|
||||
if (number >= 11 && number <= 13)
|
||||
return number + "th";
|
||||
|
||||
return number + GetSuffix(number % 10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string to title case (first letter of each word capitalized).
|
||||
/// </summary>
|
||||
/// <param name="input">The string to convert.</param>
|
||||
/// <returns>The string in title case.</returns>
|
||||
public static string ToTitleCase(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return input;
|
||||
|
||||
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(input.ToLowerInvariant());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Truncates a string to the specified maximum length, optionally adding an ellipsis.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to truncate.</param>
|
||||
/// <param name="maxLength">The maximum length of the result.</param>
|
||||
/// <param name="useEllipsis">Whether to add "..." when truncating.</param>
|
||||
/// <returns>The truncated string.</returns>
|
||||
public static string Truncate(string input, int maxLength, bool useEllipsis = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return input;
|
||||
|
||||
if (input.Length <= maxLength)
|
||||
return input;
|
||||
|
||||
if (useEllipsis && maxLength > 3)
|
||||
{
|
||||
return input.Substring(0, maxLength - 3) + "...";
|
||||
}
|
||||
|
||||
return input.Substring(0, maxLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes invalid filename characters from a string, replacing them with underscores.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename to sanitize.</param>
|
||||
/// <returns>A safe filename with invalid characters replaced.</returns>
|
||||
public static string SanitizeFileName(string filename)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
return filename;
|
||||
|
||||
var invalidChars = System.IO.Path.GetInvalidFileNameChars();
|
||||
var sb = new StringBuilder(filename);
|
||||
|
||||
foreach (var invalidChar in invalidChars)
|
||||
{
|
||||
sb.Replace(invalidChar, '_');
|
||||
}
|
||||
|
||||
// Also replace some additional problematic characters
|
||||
sb.Replace(' ', '_'); // Spaces can be problematic
|
||||
sb.Replace('"', '\''); // Double quotes to single quotes
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a string is null, empty, or contains only whitespace.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to check.</param>
|
||||
/// <returns>True if the string is null, empty, or whitespace only.</returns>
|
||||
public static bool IsNullOrWhiteSpace(string input)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely gets a substring without throwing exceptions for invalid indices.
|
||||
/// </summary>
|
||||
/// <param name="input">The source string.</param>
|
||||
/// <param name="startIndex">The starting index.</param>
|
||||
/// <param name="length">The length of the substring.</param>
|
||||
/// <returns>The substring, or empty string if indices are invalid.</returns>
|
||||
public static string SafeSubstring(string input, int startIndex, int length)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input) || startIndex < 0 || startIndex >= input.Length)
|
||||
return string.Empty;
|
||||
|
||||
var actualLength = Math.Min(length, input.Length - startIndex);
|
||||
return actualLength <= 0 ? string.Empty : input.Substring(startIndex, actualLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely gets a substring from the start index to the end of the string.
|
||||
/// </summary>
|
||||
/// <param name="input">The source string.</param>
|
||||
/// <param name="startIndex">The starting index.</param>
|
||||
/// <returns>The substring from start index to end, or empty if invalid.</returns>
|
||||
public static string SafeSubstring(string input, int startIndex)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input) || startIndex < 0 || startIndex >= input.Length)
|
||||
return string.Empty;
|
||||
|
||||
return input.Substring(startIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pads a string to a specific length, truncating if too long.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to pad or truncate.</param>
|
||||
/// <param name="totalLength">The desired total length.</param>
|
||||
/// <param name="paddingChar">The character to use for padding.</param>
|
||||
/// <param name="padLeft">True to pad on the left, false to pad on the right.</param>
|
||||
/// <returns>A string of exactly the specified length.</returns>
|
||||
public static string PadOrTruncate(string input, int totalLength, char paddingChar = ' ', bool padLeft = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
input = string.Empty;
|
||||
|
||||
if (input.Length == totalLength)
|
||||
return input;
|
||||
|
||||
if (input.Length > totalLength)
|
||||
return input.Substring(0, totalLength);
|
||||
|
||||
return padLeft
|
||||
? input.PadLeft(totalLength, paddingChar)
|
||||
: input.PadRight(totalLength, paddingChar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string to a safe identifier (letters, numbers, underscores only).
|
||||
/// </summary>
|
||||
/// <param name="input">The input string.</param>
|
||||
/// <returns>A safe identifier string.</returns>
|
||||
public static string ToSafeIdentifier(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return "Identifier";
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (char c in input)
|
||||
{
|
||||
if (char.IsLetterOrDigit(c))
|
||||
{
|
||||
sb.Append(c);
|
||||
}
|
||||
else if (c == ' ' || c == '-' || c == '.')
|
||||
{
|
||||
sb.Append('_');
|
||||
}
|
||||
}
|
||||
|
||||
var result = sb.ToString();
|
||||
|
||||
// Ensure it starts with a letter or underscore
|
||||
if (result.Length > 0 && char.IsDigit(result[0]))
|
||||
{
|
||||
result = "_" + result;
|
||||
}
|
||||
|
||||
return string.IsNullOrEmpty(result) ? "Identifier" : result;
|
||||
}
|
||||
|
||||
#region Private Helper Methods
|
||||
|
||||
private static string GetSuffix(int lastDigit)
|
||||
{
|
||||
switch (lastDigit)
|
||||
{
|
||||
case 1: return "st";
|
||||
case 2: return "nd";
|
||||
case 3: return "rd";
|
||||
default: return "th";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
using SolidWorks.Interop.sldworks;
|
||||
using ExportDXF.Extensions;
|
||||
using SolidWorks.Interop.sldworks;
|
||||
using SolidWorks.Interop.swconst;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ExportDXF
|
||||
namespace ExportDXF.Utilities
|
||||
{
|
||||
internal static class ViewHelper
|
||||
{
|
||||
@@ -301,17 +302,8 @@ namespace ExportDXF
|
||||
|
||||
public static bool HasSupressedBends(IView view)
|
||||
{
|
||||
var model = view.ReferencedDocument;
|
||||
var refConfig = view.ReferencedConfiguration;
|
||||
model.ShowConfiguration(refConfig);
|
||||
|
||||
var model = GetModelFromView(view);
|
||||
var flatPattern = model.GetFeatureByTypeName("FlatPattern");
|
||||
|
||||
if (flatPattern.IsSuppressed())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var bends = flatPattern.GetAllSubFeaturesByTypeName("UiBend");
|
||||
|
||||
foreach (var bend in bends)
|
||||
@@ -323,5 +315,14 @@ namespace ExportDXF
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ModelDoc2 GetModelFromView(IView view)
|
||||
{
|
||||
var model = view.ReferencedDocument;
|
||||
var refConfig = view.ReferencedConfiguration;
|
||||
model.ShowConfiguration(refConfig);
|
||||
|
||||
return model;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Windows.Forms;
|
||||
using ExportDXF.Utilities;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ExportDXF.ViewFlipDeciders
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using ExportDXF.Utilities;
|
||||
using System.Linq;
|
||||
|
||||
namespace ExportDXF.ViewFlipDeciders
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using ExportDXF.Utilities;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ExportDXF.ViewFlipDeciders
|
||||
|
||||
147
ExportDXF/ViewFlipDeciders/ViewFlipDeciderFactory.cs
Normal file
147
ExportDXF/ViewFlipDeciders/ViewFlipDeciderFactory.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ExportDXF.ViewFlipDeciders
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory for discovering and creating IViewFlipDecider implementations.
|
||||
/// </summary>
|
||||
public static class ViewFlipDeciderFactory
|
||||
{
|
||||
private static readonly object _lock = new object();
|
||||
private static List<IViewFlipDecider> _cachedDeciders;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all available IViewFlipDecider implementations from loaded assemblies.
|
||||
/// Results are cached for performance.
|
||||
/// </summary>
|
||||
/// <returns>An enumerable collection of IViewFlipDecider instances.</returns>
|
||||
public static IEnumerable<IViewFlipDecider> GetAvailableDeciders()
|
||||
{
|
||||
if (_cachedDeciders != null)
|
||||
return _cachedDeciders;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_cachedDeciders != null)
|
||||
return _cachedDeciders;
|
||||
|
||||
_cachedDeciders = DiscoverDeciders().ToList();
|
||||
return _cachedDeciders;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a specific view flip decider by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the decider to retrieve.</param>
|
||||
/// <returns>The matching decider, or null if not found.</returns>
|
||||
public static IViewFlipDecider GetDeciderByName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return null;
|
||||
|
||||
return GetAvailableDeciders()
|
||||
.FirstOrDefault(d => string.Equals(d.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default (Automatic) view flip decider, or the first available if Automatic doesn't exist.
|
||||
/// </summary>
|
||||
/// <returns>The default decider.</returns>
|
||||
public static IViewFlipDecider GetDefaultDecider()
|
||||
{
|
||||
var automatic = GetDeciderByName("Automatic");
|
||||
if (automatic != null)
|
||||
return automatic;
|
||||
|
||||
return GetAvailableDeciders().FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the cached deciders, forcing rediscovery on next call.
|
||||
/// Useful for testing or if assemblies are loaded dynamically.
|
||||
/// </summary>
|
||||
public static void ClearCache()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_cachedDeciders = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Discovers all IViewFlipDecider implementations in loaded assemblies.
|
||||
/// </summary>
|
||||
private static IEnumerable<IViewFlipDecider> DiscoverDeciders()
|
||||
{
|
||||
var deciderType = typeof(IViewFlipDecider);
|
||||
var discoveredTypes = new List<Type>();
|
||||
|
||||
try
|
||||
{
|
||||
// Search all loaded assemblies
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
try
|
||||
{
|
||||
var types = assembly.GetTypes()
|
||||
.Where(t => deciderType.IsAssignableFrom(t) &&
|
||||
t.IsClass &&
|
||||
!t.IsAbstract &&
|
||||
HasParameterlessConstructor(t));
|
||||
|
||||
discoveredTypes.AddRange(types);
|
||||
}
|
||||
catch (ReflectionTypeLoadException)
|
||||
{
|
||||
// Skip assemblies that can't be loaded
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If discovery fails entirely, return empty collection
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Create instances of discovered types
|
||||
foreach (var type in discoveredTypes)
|
||||
{
|
||||
IViewFlipDecider instance = null;
|
||||
|
||||
try
|
||||
{
|
||||
instance = (IViewFlipDecider)Activator.CreateInstance(type);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Skip types that can't be instantiated
|
||||
continue;
|
||||
}
|
||||
|
||||
if (instance != null)
|
||||
{
|
||||
yield return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a type has a public parameterless constructor.
|
||||
/// </summary>
|
||||
private static bool HasParameterlessConstructor(Type type)
|
||||
{
|
||||
return type.GetConstructor(
|
||||
BindingFlags.Public | BindingFlags.Instance,
|
||||
null,
|
||||
Type.EmptyTypes,
|
||||
null) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,5 +5,7 @@
|
||||
</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"/>
|
||||
</appSettings>
|
||||
</configuration>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user