using System; using System.IO; using Xarial.XCad.Documents; using Xarial.XCad.Documents.Enums; using Xarial.XCad.Enums; using Xarial.XCad.SolidWorks; using Xarial.XCad.SolidWorks.Documents; using Xarial.XCad.SolidWorks.Enums; using System.Data; using System.Linq; using Newtonsoft.Json; using SolidWorks; using System.Collections.Generic; using SolidWorks.Interop.sldworks; using CommandLine; using SolidWorks.Interop.swconst; using System.Text.RegularExpressions; namespace model_reader { class Options { [Option('s', "source", Required = true, HelpText = "Input file path")] public string Source { get; set; } [Option('t', "target", Required = true, HelpText = "Output file path")] public string Target { get; set; } [Option('c', "configuration", Required = false, HelpText = "SolidWorks configuration name", Default = "Default")] public string Configuration { get; set; } [Option('p', "pipeName", Required = false, HelpText = "Named pipe name for IPC", Default = "osr-cad")] public string PipeName { get; set; } [Option('l', "logFilePath", Required = false, HelpText = "File path to redirect SolidWorks stdout", Default = "log.txt")] public string LogFilePath { get; set; } [Option('h', "hidden", Required = false, HelpText = "Hide Solidworks window", Default = "true")] public string Hidden { get; set; } [Option('t', "tree", Required = false, HelpText = "Dump Model Tree Data", Default = "false")] public string Tree { get; set; } [Option('f', "flags", Required = false, HelpText = "Document Open Flags", Default = DocumentState_e.ReadOnly | DocumentState_e.Hidden)] public int flags{ get; set; } [Option("swv", Required = false, HelpText = "Solidworks version, 30=2022, 31=2023, 32=2024, 33=2025", Default = 33)] public int swv{ get; set; } } public class TreeItem { public string Name { get; set; } public List Children { get; set; } public string Path { get; set; } public string Parent { get; set; } public Dictionary> Properties { get; set; } public DictionaryEquations { get; set; } public DictionaryMass { get; set; } public Dictionary Box { get; set; } public DictionaryMaterial { get; set; } public Dictionary States { get; set; } public Dictionary LaserParts { get; set; } public bool IsSuppressed { get; set; } public string activeConfiguration { get; set; } } class Program { public static Dictionary GetPhysicalMaterials(ModelDoc2 modelDoc) { if (modelDoc == null) { return null; } Dictionary materialDict = new Dictionary(); try { // Check if the document is a part or assembly if (modelDoc.GetType() == (int)swDocumentTypes_e.swDocPART) { PartDoc partDoc = (PartDoc)modelDoc; string materialName = partDoc.GetMaterialPropertyName2("", out string materialDatabase); materialDict["Material"] = materialName; materialDict["Materials"] = materialDatabase; } else if (modelDoc.GetType() == (int)swDocumentTypes_e.swDocASSEMBLY) { AssemblyDoc assemblyDoc = (AssemblyDoc)modelDoc; object[] components = (object[])assemblyDoc.GetComponents(false); foreach (Component2 component in components) { ModelDoc2 componentModel = (ModelDoc2)component.GetModelDoc2(); if (componentModel != null && componentModel.GetType() == (int)swDocumentTypes_e.swDocPART) { PartDoc partDoc = (PartDoc)componentModel; string materialName = partDoc.GetMaterialPropertyName2("", out string materialDatabase); materialDict[component.Name2] = materialName; //materialDict[$"{component.Name2}-Materials"] = materialDatabase; } } } }catch(Exception ex) { Console.WriteLine("Error retrieving material: " + modelDoc.GetPathName() + " : " + ex.Message); } return materialDict; } public static Dictionary GetStates(ModelDoc2 modelDoc) { Dictionary states = new Dictionary(); try { object oFeatures; object oErrorCodes; object oWarnings; object[] Features = null; int[] ErrorCodes = null; bool[] Warnings = null; bool boolstatus = false; int i = 0; int nbrWhatsWrong = 0; Feature swFeature = default(Feature); ModelDocExtension swModelDocExt = default(ModelDocExtension); swModelDocExt = (ModelDocExtension)modelDoc.Extension; nbrWhatsWrong = swModelDocExt.GetWhatsWrongCount(); if (nbrWhatsWrong > 0) { boolstatus = swModelDocExt.GetWhatsWrong(out oFeatures, out oErrorCodes, out oWarnings); Features = (object[])oFeatures; ErrorCodes = (int[])oErrorCodes; Warnings = (bool[])oWarnings; states["wrong"] = "" + nbrWhatsWrong; states["errors"] = string.Join(" ", ErrorCodes); states["warnings"] = string.Join(" ", Warnings); List Names = new List { }; for (i = 0; i < Features.Length; i++) { swFeature = (Feature)Features[i]; Names.Add(swFeature.GetTypeName2()); } states["features"] = string.Join(" | ", Names); } } catch (Exception ex) { Console.WriteLine("Error retrieving states: " + modelDoc.GetPathName() + " : " + ex.Message); } return states; } public static Dictionary GetLaserParts(ModelDoc2 modelDoc) { Dictionary partsWithLaserProperty = new Dictionary(); /* if (modelDoc != null && modelDoc.GetType() == (int)swDocumentTypes_e.swDocPART) { PartDoc part = (PartDoc)modelDoc; bool hasLaserProperty = false; object[] bodies = (object[])part.GetBodies2((int)swBodyType_e.swAllBodies, false); foreach (Body2 body in bodies) { object[] faces = (object[])body.GetFaces(); foreach (Face2 face in faces) { var facePropMgr = face.GetUserPropertyManager(); string valOut = ""; facePropMgr.Get4("laser", false, out valOut); if (!string.IsNullOrEmpty(valOut)) { hasLaserProperty = true; break; } } if (hasLaserProperty) break; } if (hasLaserProperty) { // partsWithLaserProperty[partPath] = modelDoc.GetPathName(); } }*/ return partsWithLaserProperty; } public static Dictionary GetMassProperties(ModelDoc2 modelDoc) { if (modelDoc == null) { return null; } Dictionary massPropertiesDict = new Dictionary(); try { MassProperty massProperty = modelDoc.Extension.CreateMassProperty(); massPropertiesDict["Mass"] = massProperty.Mass; massPropertiesDict["Density"] = massProperty.Density; massPropertiesDict["Volume"] = massProperty.Volume; massPropertiesDict["SurfaceArea"] = massProperty.SurfaceArea; double[] centerOfMass = (double[])massProperty.CenterOfMass; massPropertiesDict["CenterOfMassX"] = centerOfMass[0]; massPropertiesDict["CenterOfMassY"] = centerOfMass[1]; massPropertiesDict["CenterOfMassZ"] = centerOfMass[2]; } catch (Exception ex) { Console.WriteLine("Error retrieving mass properties: " + modelDoc.GetPathName() + " : " + ex.Message); } return massPropertiesDict; } public static Dictionary GetBoundingBox(Component2 modelDoc) { if (modelDoc == null) { return null; } try { Dictionary boundingBoxDict = new Dictionary(); object bboxo = modelDoc.GetBox(false, false); if (bboxo !=null && ! System.DBNull.Value.Equals(bboxo)) { double[] boundingBox = (double[])bboxo; if (boundingBox == null || boundingBox.Length != 6) { Console.WriteLine("Failed to retrieve the bounding box."); return boundingBoxDict; } boundingBoxDict["MinX"] = (double)boundingBox[0]; boundingBoxDict["MinY"] = (double)boundingBox[1]; boundingBoxDict["MinZ"] = (double)boundingBox[2]; boundingBoxDict["MaxX"] = (double)boundingBox[3]; boundingBoxDict["MaxY"] = (double)boundingBox[4]; boundingBoxDict["MaxZ"] = (double)boundingBox[5]; return boundingBoxDict; } }catch(Exception e) { Console.WriteLine("Error retrieving bounding box: " + modelDoc.GetPathName() + " : " + e.Message); } return null; } public static Dictionary GetEquations(ModelDoc2 modelDoc) { if (modelDoc == null) { return null; } Dictionary equationDict = new Dictionary(); try { EquationMgr equationMgr = default(EquationMgr); equationMgr = (EquationMgr)modelDoc.GetEquationMgr(); int equationCount = equationMgr.GetCount(); for (int i = 0; i < equationCount; i++) { string equation = equationMgr.Equation[i]; string[] equationParts = equation.Split('='); if (equationParts.Length == 2) { string name = equationParts[0].Trim(); string pattern = "\"([^\"]*)\""; Regex regex = new Regex(pattern); Match match = regex.Match(name); if (match.Success) { name = match.Groups[1].Value; } double evalValue = equationMgr.get_Value(i); equationDict[name] = evalValue; } } } catch (Exception e) { Console.WriteLine("Error retrieving equations: " + modelDoc.GetPathName() + " : " + e.Message); } return equationDict; } static void TraverseComponents(object[] components, List parentList, Options options) { foreach (Component2 component in components) { if (component == null) continue; TreeItem item = new TreeItem(); item.Name = component.Name2; item.Path = component.GetPathName(); item.IsSuppressed = component.IsSuppressed(); ModelDoc2 model = (ModelDoc2)component.GetModelDoc2(); if (model != null) { item.Properties = getProperties2(model); item.Equations = GetEquations(model); item.Mass = GetMassProperties(model); item.Material = GetPhysicalMaterials(model); item.Box = GetBoundingBox(component); item.States = GetStates(model); } object[] vChildComp = (object[])component.GetChildren(); if (vChildComp != null && vChildComp.Length > 0) { List children = new List(); TraverseComponents(vChildComp, children, options); item.Children = children; foreach (TreeItem child in children) { child.Parent = item.Path; } } parentList.Add(item); } } public class ComponentData { public string Name { get; set; } public string Path { get; set; } public bool IsSuppressed { get; set; } } public class AssemblyData { public List Components { get; set; } = new List(); } public class Tree { public AssemblyData assembly; public TreeItem root; public Dictionary> Configurations { get; set; } } public static AssemblyData GetAssemblyData(AssemblyDoc swAssembly) { var assemblyData = new AssemblyData(); ModelDoc2 m = (ModelDoc2)swAssembly; object[] components = (object[])swAssembly.GetComponents(false); foreach (object component in components) { const string FILE_PATH_COLUMN_NAME = "File Path"; var prpsTable = new DataTable(); prpsTable.Columns.Add(FILE_PATH_COLUMN_NAME); Component2 comp = (Component2)component; var compData = new ComponentData { Name = ((Component2)component).Name2, Path = ((Component2)component).GetPathName(), IsSuppressed = ((Component2)component).IsSuppressed() }; assemblyData.Components.Add(compData); } return assemblyData; } static Dictionary GetCustomProperties(IConfiguration configuration) { var customProperties = new Dictionary(); var propertyManager = configuration.CustomPropertyManager as ICustomPropertyManager; var propertyNames = propertyManager.GetNames() as object[]; if (propertyNames != null) { foreach (var propertyName in propertyNames) { var propName = propertyName.ToString(); var propValue = propertyManager.Get(propName).ToString(); string evaluated = ""; string value= ""; bool status = propertyManager.Get4(propName, false, out value, out evaluated); customProperties.Add(propName, evaluated); } } return customProperties; } static Dictionary> getProperties2(ModelDoc2 swModel) { if (swModel == null) { return null; } var configurationsWithProperties = new Dictionary>(); try { List configurations = new List(); SolidWorks.Interop.sldworks.ConfigurationManager swConfigMgr = swModel.ConfigurationManager; object[] configNameArr = null; configNameArr = (object[])swModel.GetConfigurationNames(); string configName = null; SolidWorks.Interop.sldworks.Configuration swConfig = default(SolidWorks.Interop.sldworks.Configuration); bool status = false; int i = 0; for (i = 0; i <= configNameArr.GetUpperBound(0); i++) { configName = (string)configNameArr[i]; swConfig = (SolidWorks.Interop.sldworks.Configuration)swModel.GetConfigurationByName(configName); var customProperties = GetCustomProperties(swConfig); configurationsWithProperties.Add(configName, customProperties); } }catch (Exception ex) { Console.WriteLine("Error retrieving properties2: " + swModel.GetPathName() + " : " + ex.Message); } try { bool globalProperties = true; if (globalProperties) { CustomPropertyManager customPropertyManagerAll = swModel.Extension.CustomPropertyManager[""]; string[] propertyNamesAll = (string[])customPropertyManagerAll.GetNames(); if (propertyNamesAll != null) { var customProperties = new Dictionary(); configurationsWithProperties.Add("Global", customProperties); foreach (var propertyName in propertyNamesAll) { var propName = propertyName.ToString(); var propValue = customPropertyManagerAll.Get(propName).ToString(); customProperties.Add(propName, propValue); } } } } catch (Exception ex) { Console.WriteLine("Error retrieving properties2 : " + swModel.GetPathName() + " : " + ex.Message); } /* foreach (string propertyName in propertyNamesAll) { string propertyValue; string resolvedValue; bool wasResolved; customPropertyManagerAll.Get4(propertyName, false, out propertyValue, out resolvedValue); Console.WriteLine($"Property: {propertyName}, Value: {propertyValue}, Resolved Value: {resolvedValue}"); }*/ return configurationsWithProperties; } static TreeItem CreateAssemblyTreeJson(ModelDoc2 swModel,Options options) { AssemblyDoc swAssembly = (AssemblyDoc)swModel; TreeItem root = new TreeItem(); try { root.Name = swModel.GetTitle(); root.Path = swModel.GetPathName(); root.Properties = getProperties2(swModel); root.Equations = GetEquations(swModel); root.Mass = GetMassProperties(swModel); root.Material = GetPhysicalMaterials(swModel); root.States = GetStates(swModel); //root.IsSuppressed = ((Component2)swModel).IsSuppressed(); ConfigurationManager configMgr = swModel.ConfigurationManager; Configuration activeConfig = configMgr.ActiveConfiguration; root.activeConfiguration = activeConfig.Name; List children = new List(); object[] components = (object[])swAssembly.GetComponents(false); TraverseComponents(components, children, options); root.Children = children; } catch (Exception ex) { Console.WriteLine("Error CreateAssemblyTreeJson: " + swModel.GetPathName() + " : " + ex.Message); } return root; } static Tree TreeMeta(ModelDoc2 swModel, Options options) { string targetJsonPath = options.Target.Replace(".json",".tree.json"); AssemblyDoc swAssembly = (AssemblyDoc)swModel; AssemblyData assemblyData = GetAssemblyData(swAssembly); Tree tree = new Tree(); tree.root = CreateAssemblyTreeJson(swModel, options); tree.assembly = assemblyData; tree.Configurations = getProperties2(swModel); try { string json = JsonConvert.SerializeObject(tree, Formatting.Indented, new JsonConverter[] { new Newtonsoft.Json.Converters.StringEnumConverter() }); File.WriteAllText(targetJsonPath, json); Console.WriteLine("<>", targetJsonPath); } catch (Exception ex) { Console.WriteLine("<>",ex.Message); } return tree; } static void Run(Options options) { int version = options.swv; using (var app = SwApplicationFactory.Create((SwVersion_e)version, ApplicationState_e.Background)) { var input = options.Source; var output = options.Target; var file = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location)); var doc = (ISwDocument)app.Documents.Open(input, (DocumentState_e)options.flags); ModelDoc2 swModel = (ModelDoc2)doc.Model; if (!swModel.ShowConfiguration2(options.Configuration)) { Console.WriteLine($"<>"); } else { Console.WriteLine($"<>"); } const string FILE_PATH_COLUMN_NAME = "File Path"; var prpsTable = new DataTable(); prpsTable.Columns.Add(FILE_PATH_COLUMN_NAME); var assm = (IXAssembly)doc; var refDocs = assm.Configurations.Active.Components.Flatten().GroupBy(x => { try { return x.Path; } catch { Console.WriteLine($"<>"); return ""; } }).Where(x => !string.IsNullOrEmpty(x.Key)).Select(x => x.First().Document).Where(x => x.IsCommitted).ToList(); refDocs = refDocs.Prepend(assm).ToList(); foreach (var refDoc in refDocs) { if (refDoc.Path.Contains("IC~~")) { continue; } var row = prpsTable.NewRow(); row[FILE_PATH_COLUMN_NAME] = refDoc.Path; //active props foreach (var prp in refDoc.Configurations.Active.Properties) { try { if (prp.Value == null || String.IsNullOrEmpty(prp.Value.ToString()) || prp.Value == DBNull.Value) { continue; } } catch (Exception ex) { Console.WriteLine($"<>"); continue; } var col = prpsTable.Columns[prp.Name]; if (col == null) { col = prpsTable.Columns.Add(prp.Name); } row[prp.Name] = prp.Value; } // all props foreach (var prp in refDoc.Properties) { try { try { if (prp.Value == null || String.IsNullOrEmpty(prp.Value.ToString()) || prp.Value == DBNull.Value) { continue; } } catch (Exception ex) { Console.WriteLine($"<>"); continue; } var col = prpsTable.Columns[prp.Name]; if (col == null) { col = prpsTable.Columns.Add(prp.Name); } } catch { Console.WriteLine("<>", prp.Name); } } prpsTable.Rows.Add(row); } try { string json = JsonConvert.SerializeObject(prpsTable, Formatting.Indented, new JsonConverter[] { new Newtonsoft.Json.Converters.StringEnumConverter() }); File.WriteAllText(output, json); Console.WriteLine("<>", output); } catch (Exception ex) { Console.WriteLine("<>", ex.Message); } TreeMeta(swModel, options); doc.Close(); app.Close(); } } static void HandleParseErrors(System.Collections.Generic.IEnumerable errors) { Console.WriteLine("<>"); } static void Main(string[] args) { Parser.Default.ParseArguments(args) .WithParsed(options => Run(options)) .WithNotParsed(errors => HandleParseErrors(errors)); } } }