cad port 1/3

This commit is contained in:
Code 2025-02-06 21:52:16 +01:00
parent 88449f52c9
commit 76f2452e98
767 changed files with 261115 additions and 0 deletions

449
packages/cad/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,449 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Test - Drive - HTML",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=false",
"--dry=false",
"--logLevel=trace",
"--configuration=test",
"--src='./tests/drive/400*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}-${CONFIGURATION}.+(html)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test - Edrawings",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=true",
"--dry=false",
"--debug=true",
"--verbose=true",
"--src='./tests/html/*.+(SLDASM|SLDPRT)'",
"--dst='${SRC_DIR}/${SRC_NAME}.+(html)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test - Drive - BOM",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=false",
"--dry=false",
"--debug=true",
"--verbose=true",
"--src='./tests/drive/*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}.+(xlsx)'"
]
},
{
"type": "node",
"request": "launch",
"name": "Test - Pack - Cabinet",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=false",
"--pack=true",
"--logLevel=debug",
"--src='./tests/cabinet/vintage*.+(SLDASM)'",
"--dst='${SRC_DIR}/packed'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test:Drive:JSON",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=false",
"--logLevel=trace",
"--src='./tests/drive/*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}.+(json)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test - ModelReader - Lydia36",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=true",
"--dry=false",
"--debug=true",
"--verbose=true",
"--src='${OSR_ROOT}/products/products/extrusion/lydia-v3.5/cad/*Global*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}.+(json)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test - Drive - Props - Cas",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=true",
"--dry=false",
"--debug=true",
"--verbose=true",
"--src='./tests/cas/Global*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}.+(json)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test - Drive - STEP",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--debug=true",
"--hidden=false",
"--verbose=true",
"--src='./tests/drive/*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}.+(jpg)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test - Drive - JPG - PhotoView",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=true",
"--dry=false",
"--debug=true",
"--verbose=true",
"--src='./tests/drive/*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}.+(jpg)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test - CAS - STEP:NoEnclosure",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=true",
"--dry=false",
"--debug=true",
"--verbose=true",
"--src='./tests/cas/cad/Global*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}.+(step)'",
//"--configuration='NoEnclosure'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test - CAS - STEP: All Configurations",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=true",
"--dry=false",
"--debug=true",
"--verbose=true",
"--src='./tests/cas/cad/Global*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}-${CONFIGURATION}.+(step|html)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test - Flexibot - STEP: All Configurations",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=true",
"--dry=false",
"--debug=true",
"--verbose=true",
"--src='${OSR_ROOT}/flexi-bot/cad/storch-150/cad/Global*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}-${CONFIGURATION}.+(step)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test - Drive - Configrations",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=true",
"--dry=false",
"--debug=true",
"--verbose=true",
"--src='./tests/drive/*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}-configs.+(json)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test - CAS - Configrations to HTML",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=true",
"--dry=false",
"--debug=true",
"--verbose=true",
"--src='./tests/cas/cad/Global*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}-${CONFIGURATION}.+(html)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test Alt - Drive - JSON",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=true",
"--dry=false",
"--debug=true",
"--verbose=true",
"--alt=true",
"--src='./tests/drive/*.+(SLDASM)'",
"--dst='&{SRC_DIR}/&{SRC_NAME}.+(json)'"
]
},
{
"type": "node",
"request": "launch",
"name": "Test PRoot/Katbot - JSON",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=true",
"--dry=false",
"--debug=true",
"--verbose=true",
"--alt=false",
"--src='${PRODUCT_ROOT}/products/injection/katbot/cad/Global*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}.+(json)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test PRoot/SPRCB650 - JSON",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"sw",
"--cache=true",
"--dry=false",
"--debug=true",
"--verbose=true",
"--alt=false",
"--src='${PRODUCT_ROOT}/products/sheetpress/sheetpress-cell-rcA-x/cad/Global*.+(SLDASM)'",
"--dst='${SRC_DIR}/${SRC_NAME}.+(json)'"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Test - Slice",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"slice",
"--cache=false",
"--dry=false",
"--debug=true",
"--verbose=true",
"--src='./tests/print/Global*.+(3mf)'",
"--dst='${SRC_DIR}/${SRC_NAME}.+(gcode)'",
"--saveAsProfile='${SRC_DIR}/profile.json'",
"--profile='${SRC_DIR}/profile.json'",
"--saveArgs='${SRC_DIR}/slice.sh'",
"--log='${SRC_DIR}/last.log'",
]
},
{
"type": "node",
"request": "launch",
"name": "Test - Slice - Profiles",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}//**/*.js"
],
"args": [
"slice",
"--cache=false",
"--dry=false",
"--debug=true",
"--verbose=true",
"--src='./tests/print/Global*.+(3mf)'",
"--dst='${SRC_DIR}/${SRC_NAME}.+(gcode)'",
"--saveAsProfile='${SRC_DIR}/profile.json'",
"--profile='${OSR_PROFILES}/print/tests/profile.json'",
"--saveArgs='${SRC_DIR}/slice.sh'",
"--log='${SRC_DIR}/last.log'",
]
}
]
}

6
packages/cad/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"cSpell.words": [
"Solidwork"
],
"cmake.configureOnOpen": false
}

24
packages/cad/LICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>

214
packages/cad/README.md Normal file
View File

@ -0,0 +1,214 @@
# OSR CAD Tools
This is a CLI(CommandLineInterface) toolset to convert 3D files, using Solidworks and other software.
## Requirements
1. [Node-JS](https://nodejs.org/en/download/)
2. Optional: install [Git](https://git-scm.com/downloads) (Make sure you enable Linux tools on Windows console)
3. Solidworks 2020. In case you are using another version, please find on your disc 'SolidWorks.Interop.sldworks.dll' and replace the one in [./sw/2022](https://git.osr-plastic.org/osr-plastic/osr-cad/src/branch/master/sw/2022)
## Installation
```sh
git clone https://git.osr-plastic.org/osr-plastic/osr-cad.git
cd osr-convert-cad
npm i
# or globally (recommended)
npm i @plastichub/osr-cad -g
```
## Usage
Open a terminal and run this:
```sh
osr-cad --help
```
See more in [./docs/Examples.md](./docs/Examples.md) and [./docs/Integration.md](./docs/Integration.md)
### References - Development
- [site:Solidworks API basics - examples](https://www.codestack.net/labs/solidworks/)
- [site:Rhino-API](https://developer.rhino3d.com/api/RhinoCommon/html/R_Project_RhinoCommon.htm)
- [site:Solidworks Reverse Decoder](http://heybryan.org/solidworks_file_format.html)
- [sw interop - component - API ](https://help.solidworks.com/2019/English/api/swdocmgrapi/Get_Current_Name_of_Configuration_of_Suppressed_Component_Example_CSharp.htm)
## Todos
- [x] Select default views via CLI Argument
- [ ] Arg: Skip suppressed | hidden (difficult since it's out of part file scope, check explorer api ) | dry mode
- [x] Arg: Overwrite files
- [-] Arg: skip non OSR parts
- [-] Arg: displaymode : wireframe, shaded, ... (see [SW Docs](http://help.solidworks.com/2017/english/api/sldworksapi/solidworks.interop.sldworks~solidworks.interop.sldworks.iview~setdisplaymode3.html))
- [-] report
- [x] export as lib
- [-] Multi view (trainings data for @plastichub/part-detector)
- [-] Speed: use same instance for multiple exports
- [-] Context Menu Shell Extension (@osr-tools)
- [-] Local/Global config (=>osrl)
- [ ] emit/merge authors from components in target artefact
- [-] Add CLI Arg Path variables
- [-] json-path for glob patterns
- [-] bracket expansion
- [-] Report templates (=> @osrl | osr-reports)
- [ ] xls
- [ ] md
- [ ] txt
- [ ] json
- [ ] meta | ts | tree
- [-] Plugin interface for custom format (chained) => osrl
- [x] Conversions
- [x] STEP -> SLDPRT (via xcad->fc->fw)
- [x] any -> 3dxml (osrl!)
- [x] any -> html (via edrawings)
- [ ] Structural
- [ ] support pipes, eg: intermediate formats
- [-] add pre, post and content filters, as pipes
- [-] plugins
- [-] integrate osrl
- [-] omit format options in --help
- [ ] omit possible conversions in ```info``
- [ ] per in and out args
- [ ] global
- [-] Cache hash fuckery : integrate options in integrity
- [ ] external cache directory
- [ ] bom|html|props : configurations
- [ ] explicit cache directory
### Commands - Todos - Solidworks
- [x] Solidworks
- [-] Set system wide options for JPG output
- [-] Set system wide options for PDF output
- [-] Directory index (=>osrl)
- [ ] arg: local HTML path/dir offset
- [ ] arg: generate UNC paths
- [ ] format: PDF
- [-] arg: sw drawing/BOMs to CSV/xls
- [-] Part/Sub-Assembly web(&local) compilation/index (=>osrl)
- [-] Web directory ([xeokit](https://gitlab.com/plastichub/osr/xeokit-sdk))
- [-] Git hook, check components & references
- [x] move 2D formats to osr-media
- [-] implement osr-cli-common specs: info/introspect|supported
- [x] root offset
- [x] sw:info|bom -> i18n
- [ ] remove toolbox write protection (sldsetdocprop.exe | https://www.youtube.com/watch?v=N7_HSvWPAXw | https://help.solidworks.com/2022/english/api/swdocmgrapi/SolidWorks.Interop.swdocumentmgr~SolidWorks.Interop.swdocumentmgr.ISwDMDocument~ToolboxPart.html)
- [-] sw: catch read errors
- [ ] sw: migrate xcad latest
- [ ] sw: addons - API (disable, ..)
- [-] sw: model-reader : iterator | filter | findup-references
- [-] sw: bom : filter & map
- [-] sw: set properties | save-as, ...
- [ ] sw: osr-log
- [ ] sw: sub commands: convert | validate | pack | set | get | clean | render | tree (cp/mv/rm) | unlink
- [x] get: configurations -> json
- [x] convert(conf) -> model
- [x] render(conf) -> image
- [ ] validate
- [-] internal files
- [ ] outside root / component
- [ ] naming conventions
- [-] default configurations
- [ ] sw errors
- [ ] library compat
- [-] orphan files
- [-] equations
- [ ] tree
- [ ] md
- [x] json
- [ ] fs
- [-] xls
- [ ] osr-i8n
- [ ] walker -> piped
- [ ] orphans (incl. assets / maps)
- [x] sw: render : renderers (+options)
- [x] sw: render
- [-] scene defaults
- [ ] motion analysis
- [x] query(tree)
- [ ] sw: explode
- [ ] sw: timeouts
- [x] sw: osr-default props
- [-] sw: cache instance (node IPC | csharp JIT?)
- [x] sw: cache
- [x] meta: diff
- [ ] invalidate
- [-] mv
- [ ] pack / unpack
- [ ] versioning
- [ ] packages
- [ ] registry
- [ ] shared equations (design table alternative) -> osrl | equation templates
- [ ] sketch / block templates
### Commands - Todos - SCad
- [ ] impl. basic verbs: convert (see https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Using_OpenSCAD_in_a_command_line_environment)
### Lib - Todos
- [-] SW: 4 view single image
- [-] Incorrect JPG output with sw2020
### Utils
- [Batch Export to HTML via EDrawings OCX](ref/edrawings-api/BatchExportHTML)
- For SOLIDWORKS Document Manager API, please check the intro [here](https://www.codestack.net/solidworks-document-manager-api/) which leads to [https://xcad.xarial.com/]. Their actual API code is now at [./ref/xcad](./ref/xcad). Please check also [xcad basics on YouTube](https://www.youtube.com/watch?v=dLjlTYYeMpo)
### Resources - Solidworks
- [Custom Properties Write API](https://help.solidworks.com/2017/english/api/sldworksapi/change_configuration_properties_example_vb.htm)
- [XCad Github](https://github.com/xarial/xcad.git)
- [SOLIDWORKS](https://www.linkedin.com/company/solidworks?trk=public_post_share-update_update-text)
- [SW Model Error Ref](https://help.solidworks.com/2019/english/api/swconst/SO_Messages.htm)
- API Help Files [https://lnkd.in/d9QX6wvS](https://lnkd.in/d9QX6wvS?trk=public_post_share-update_update-text)
- Free API Books, Macros and Utilities by [Luke Malpass](https://uk.linkedin.com/in/angelsix?trk=public_post_share-update_update-text) - [https://lnkd.in/d8EbSDiB](https://lnkd.in/d8EbSDiB?trk=public_post_share-update_update-text)
- Video Tutorials (first few lessons are free and rest are paid) by [SolidProfessor](https://www.linkedin.com/company/solidprofessor?trk=public_post_share-update_update-text)
- [https://lnkd.in/d6bJew-z](https://lnkd.in/d6bJew-z?trk=public_post_share-update_update-text)
- [https://lnkd.in/dAv2366P](https://lnkd.in/dAv2366P?trk=public_post_share-update_update-text)
- [Artem Taturevych](https://au.linkedin.com/in/artem-taturevych?trk=public_post_share-update_update-text)s free SOLIDWORKS Goodies
- [http://www.codestack.net](http://www.codestack.net/?trk=public_post_share-update_update-text)/
- [Lenny Kikstra](https://www.linkedin.com/in/lennyworks?trk=public_post_share-update_update-text)
- free SOLIDWORKS Goodies [https://lnkd.in/d6RJfCuZ](https://lnkd.in/d6RJfCuZ?trk=public_post_share-update_update-text)
- [Roland Schwarz](https://www.linkedin.com/in/rolandschwarz?trk=public_post_share-update_update-text)
- free SOLIDWORKS Goodies [https://lnkd.in/dSiq6r6h](https://lnkd.in/dSiq6r6h?trk=public_post_share-update_update-text)
- Video Tutorials (free and paid with macros library) by [Keith Rice](https://www.linkedin.com/in/keitharice?trk=public_post_share-update_update-text)
- [https://www.cadsharp.com](https://www.cadsharp.com/?trk=public_post_share-update_update-text)
- MySolidWorks Video Training (paid) [https://lnkd.in/dpXnNBsy](https://lnkd.in/dpXnNBsy?trk=public_post_share-update_update-text)
- SOLIDWORKS Free Macros at Cadforum: [https://lnkd.in/d4W63jBX](https://lnkd.in/d4W63jBX?trk=public_post_share-update_update-text)
- SOLIDWORKS Free Macros at 3D Content Central: [https://lnkd.in/d4zVEfhh](https://lnkd.in/d4zVEfhh?trk=public_post_share-update_update-text) • SOLIDWORKS Customization eBook using VB.Net (paid) by [Tushar Suradkar](https://in.linkedin.com/in/tusharsuradkar?trk=public_post_share-update_update-text)
- [https://lnkd.in/dD_sn3ai](https://lnkd.in/dD_sn3ai?trk=public_post_share-update_update-text)
- [Mike Spens](https://www.linkedin.com/in/mikespens?trk=public_post_share-update_update-text)
- API resources [http://www.solidapi.com](http://www.solidapi.com/?trk=public_post_share-update_update-text)/ and book (paid) by him "Automating SOLIDWORKS Using Macros" ([https://amzn.to/3nWOmYn](https://amzn.to/3nWOmYn?trk=public_post_share-update_update-text))
- Stefan Berlitz's free SOLIDWORKS Goodies [https://lnkd.in/dMCmnX6h](https://lnkd.in/dMCmnX6h?trk=public_post_share-update_update-text)
- SOLIDWORKS users on active subscription also have access to two API SolidPractices available from [https://lnkd.in/d9VD3f5A](https://lnkd.in/d9VD3f5A?trk=public_post_share-update_update-text)
- Free SOLIDWORKS API [VBA + C#] Tutorials from [Prashant Baher](https://in.linkedin.com/in/prashantbaher?trk=public_post_share-update_update-text) [https://thecadcoder.com](https://thecadcoder.com/?trk=public_post_share-update_update-text)/
- Video Tutorials by [GoEngineer](https://www.linkedin.com/company/goengineer?trk=public_post_share-update_update-text) [https://lnkd.in/gfBKmeU4](https://lnkd.in/gfBKmeU4?trk=public_post_share-update_update-text)
- SOLIDWORKS forums to ask/find great solutions/macros 3DSwym SOLIDWORKS User Forum: [https://lnkd.in/dFG_isCJ](https://lnkd.in/dFG_isCJ?trk=public_post_share-update_update-text)
- Eng-Tips: [https://lnkd.in/dgspDQ-H](https://lnkd.in/dgspDQ-H?trk=public_post_share-update_update-text)
- CadOverFlow: [https://lnkd.in/d3bFWXUb](https://lnkd.in/d3bFWXUb?trk=public_post_share-update_update-text)
### XEO
- https://github.com/xeokit/xeokit-convert
### Free - CAD
- https://forum.freecadweb.org/viewtopic.php?p=556013#p556013
- https://hub.docker.com/r/amrit3701/freecad-cli
- https://wiki.opensourceecology.org/wiki/FreeCAD_BOM_Generator
### GEO
- https://macwright.com/2023/11/13/placemark
### Exce Build Dependencies for 'nexe'
- [NASM](https://www.nasm.us/pub/nasm/releasebuilds/2.16rc12/win64/)

5
packages/cad/config.json Normal file
View File

@ -0,0 +1,5 @@
{
"variables":{
"OSR_CAD_WEB":"https://osr-plastic.org/cad"
}
}

View File

@ -0,0 +1,170 @@
### General usage
```sh
osr-cad sw --src=(FOLDER||FILE)/GLOB --dst=EXPRESSION||FILE||FOLDER/GLOB
```
### Parameters
**src** : The source directory or file. This can be a glob pattern.
**dst** : The source directory or file. This can be a glob pattern with expressions.
**configuration** : The model configuration
### Variables
**SRC_DIR** : The directory of the current file being converted
**SRC_NAME** : The file name of the current file being converted
**SRC_FILE_EXT** : The file extension of the current file being converted
## Basics
### Convert all assembly files to PDF files in the current directory
```sh
osr-cad sw --src='../plastichub/products/elena/cad/*.SLDASM' --dst='${SRC_NAME}.pdf'
```
### Convert all assembly files to PDF files in the source directory
```sh
osr-cad sw --src='../plastichub/products/elena/cad/*.SLDASM' --dst='${SRC_DIR}/${SRC_NAME}.pdf'
```
### Convert all assembly files to PDF files in the source directory, recursively
**Note** : Recursion can be added by using `**/`.
```sh
osr-cad sw --src='../plastichub/products/elena/cad/**/*.SLDASM' --dst='${SRC_DIR}/${SRC_NAME}.pdf'
```
### Convert all assembly and part files to PDF files in the source directory, recursively
**Note** : Recursion can be added by using `**/`.
**Note** : To select or use multiple file extensions, write ```*.+(SLDASM|SLDPRT)``` instead of ```*.SLDASM```
```sh
osr-cad sw --src='../plastichub/products/elena/cad/**/*.+(SLDASM|SLDPRT)' --dst='${SRC_DIR}/${SRC_NAME}.pdf'
```
### Convert all assembly and part files to PDF and JPG files in the source directory, recursively
**Note** : Recursion can be added by using `**/`.
**Note** : To select or use multiple file extensions, write ```*.+(SLDASM|SLDPRT)``` instead of ```*.SLDASM```
```sh
osr-cad sw --src='../plastichub/products/elena/cad/**/*.+(SLDASM|SLDPRT)' --dst='${SRC_DIR}/${SRC_NAME}.+(pdf|jpg)'
```
### Convert all assembly files to STEP and PDF files in the source directory
```sh
osr-cad sw --src='./products/asterix-pp/cad/*.+(SLDASM)' --dst='${SRC_DIR}/${SRC_NAME}.+(step|pdf)'
```
### Extra all custom properties and depending parts from assembly files to JSON files in the source directory
```sh
osr-cad sw --src='./products/asterix-pp/cad/*.+(SLDASM)' --dst='${SRC_DIR}/${SRC_NAME}.+(json)'
```
### Convert parts or assemblies to HTML files (all incl. view and data) - using eDrawings interop API
```sh
osr-cad sw --src='./products/asterix-pp/cad/*.+(SLDASM)' --dst='${SRC_DIR}/${SRC_NAME}.+(html)'
```
### Extract all configurations and their custom properties to a JSON file
**Note** : append the destination path with `-configs.json` ! It accepts only `SLDASM` as source!
```sh
osr-cad sw --src='./products/asterix-pp/cad/*.+(SLDASM)' --dst='${SRC_DIR}/${SRC_NAME}-configs.+(json)'
```
### Convert all assembly configurations to step & HTML
**Note** : append the destination path with `${CONFIGURATION}` to enumerate through all configurations ! It accepts only `SLDASM` as source!
```sh
osr-cad sw --src='./products/asterix-pp/cad/*.+(SLDASM)' --dst='${SRC_DIR}/${SRC_NAME}-${CONFIGURATION}.+(step|html)'
```
### Export drawio files to png, pdf or jpg
**Remarks**
- make sure that draw.io.exe is being found globally (add the path to Draw.io to your Environment path variable! )
- Draw.io can be downloaded here [https://github.com/jgraph/drawio-desktop/releases/tag/v14.6.13](https://github.com/jgraph/drawio-desktop/releases/tag/v14.6.13)
- to see more options, please run ```draw.io.exe --help```: (forward the arguments using ```--args='-t'```)
```bash
Usage: draw.io [options] [input file/folder]
Options:
-V, --version output the version number
-c, --create creates a new empty file if no file is
passed
-k, --check does not overwrite existing files
-x, --export export the input file/folder based on the
given options
-r, --recursive for a folder input, recursively convert
all files in sub-folders also
-o, --output <output file/folder> specify the output file/folder. If
omitted, the input file name is used for
output with the specified format as
extension
-f, --format <format> if output file name extension is
specified, this option is ignored (file
type is determined from output extension,
possible export formats are pdf, png, jpg,
svg, vsdx, and xml) (default: "pdf")
-q, --quality <quality> output image quality for JPEG (default:
90)
-t, --transparent set transparent background for PNG
-e, --embed-diagram includes a copy of the diagram (for PNG
format only)
-b, --border <border> sets the border width around the diagram
(default: 0)
-s, --scale <scale> scales the diagram size
--width <width> fits the generated image/pdf into the
specified width, preserves aspect ratio.
--height <height> fits the generated image/pdf into the
specified height, preserves aspect ratio.
--crop crops PDF to diagram size
-a, --all-pages export all pages (for PDF format only)
-p, --page-index <pageIndex> selects a specific page, if not specified
and the format is an image, the first page
is selected
-g, --page-range <from>..<to> selects a page range (for PDF format only)
-u, --uncompressed Uncompressed XML output (for XML format
only)
-h, --help display help for command
```
```sh
osr-cad sw --src='./products/extrusion/**/*.+(drawio)' --dst='${SRC_DIR}/${SRC_NAME}.+(png)'
```
### Create & export BOMs from assembly files
**Remarks**
- it's using by default osr-cad/sw/bom-all.sldbomtbt as table template
- run osr-cad --help to see the BOM options
```sh
osr-cad sw --src='./products/extrusion/**/*.+(SLDASM)' --dst='${SRC_DIR}/${SRC_NAME}.+(xlsx)'
```
### Pack Assembly (aka 'pack and go')
```sh
osr-cad pack --src=../../ph3/products/products/injection/elena/cad/Global*.SLDASM --dst="../test"
```

View File

@ -0,0 +1,223 @@
# Integration
## Integration: [Alt-Tap Salamand](https://www.altap.cz/) - Custom Menus
To use OSR-CAD in custom menus, as follows
![](./assets/integration-as-custom-menu.PNG)
1. install osr-cad via ```npm i -g @plastichub/osr-cad```
2. Register a new custom menu (press F9 on any file)
![](./assets/integration-as-custom-menu-register.PNG)
**command** : ```osr-cad```
**Arguments** : ```sw --alt=true --verbose=true --hash=false --debug=true --skip=false --src="$(FullName)" --dst="&{SRC_DIR}/&{SRC_NAME}.+(step)"```
Here explained,
```sh
sw # osr-cad command
--alt=true # use alternate tokenizer, '&' instead of '$' to prevent collisions with Alt-Tab's own variable designator
--verbose=true # be verbose
--hash=false # don't create hash files (for caching)
--debug=true # be even more verbose
--skip=false # skip already created files
--src="$(FullName)" # use Alt-Tab's variable for the current selected file
--dst="&{SRC_DIR}/&{SRC_NAME}.+(step)" # the output destination path
```
### Remarks
- This will convert any Solidwork supported file format to ```step```. For drawings, assemblies and parts, ```jpg```, ```pdf``` and other 2D formats are supported. Conversions will use the options set in your Solidworks settings.
- Multiple selections will be excecuted serial
## Integration: Custom Grunt Task
```js
// eg: cad-convert.js
const fg = require('fast-glob');
const cad = require('@plastichub/osr-cad/cad/sw-lib');
const cadArgsSanitize = require('@plastichub/osr-cad/argv').sanitize;
const cadArgsSanitizeSingle = require('@plastichub/osr-cad/argv').sanitizeSingle;
const BPromise = require('bluebird');
const {
option
} = require('grunt');
const path = require('path');
const GLOB_MAIN_ASSEMBLY = "cad/*Global*.+(SLDASM)";
const create_sync_args = (root, product, input_glob, output_glob, options) => {
const src = `${root}/${product}/${input_glob}`;
const dst = output_glob;
return {
src,
dst,
debug: options.debug,
args: "",
hash: true,
verbose: options.verbose,
skip: options.skip,
cwd: path.resolve(root)
}
}
const create_sync_args_single = (root, product, input_glob, output_glob, options) => {
const src = `${root}/${product}/${input_glob}`;
const dst = path.resolve(`${root}/${product}/${output_glob}`);
return {
src,
dst,
debug: options.debug,
args: "",
hash: true,
verbose: options.verbose,
skip: options.skip,
cwd: path.resolve(root)
}
}
const createMeta = (root, product, options, input, output) => {
const args = create_sync_args(root, product, GLOB_MAIN_ASSEMBLY, output, options);
const syncArgs = cadArgsSanitize({
...args,
...options
});
return cad.convert(syncArgs);
}
const convert = async (items, options, input, output) => {
return BPromise.resolve(items).map((target) => {
return createMeta(options.cwd || '.', target, options, input, output);
}, {
concurrency: 1
});
}
const packAssembly = (root, product, options, input, output) => {
const args = create_sync_args_single(root, product, GLOB_MAIN_ASSEMBLY, output, options);
const syncArgs = cadArgsSanitizeSingle({
...args,
...options
});
return cad.pack(syncArgs);
}
const pack = async (items, options, input, output) => {
return BPromise.resolve(items).map((target) => {
return packAssembly(options.cwd || '.', target, options, input, output);
}, {
concurrency: 1
});
}
module.exports = function (grunt) {
const log = grunt.verbose.writeln;
grunt.registerMultiTask('cad-convert', 'Convert SW files to ... ', function () {
const done = this.async();
convert(this.data.items, {
verbose: grunt.option('verbose') !== undefined ? grunt.option('verbose') : false,
skip: grunt.option('skip') !== undefined ? grunt.option('skip') : true,
cwd: grunt.option('cwd'),
debug: grunt.option('debug') !== undefined ? grunt.option('debug') : false,
}, this.data.input || GLOB_MAIN_ASSEMBLY, this.data.output).then(() => {
done();
});
});
grunt.registerMultiTask('cad-pack', 'Pack and go SW assembly files to ... ', function () {
const done = this.async();
pack(this.data.items, {
verbose: grunt.option('verbose') !== undefined ? grunt.option('verbose') : false,
skip: grunt.option('skip') !== undefined ? grunt.option('skip') : true,
cwd: grunt.option('cwd'),
debug: grunt.option('debug') !== undefined ? grunt.option('debug') : false,
}, this.data.input || GLOB_MAIN_ASSEMBLY, this.data.output).then(() => {
done();
});
});
};
```
Now extend the Grunt configuration for different conversion tasks.
```js
'cad-convert': {
json: {
items: products,
output: '${SRC_DIR}/${SRC_NAME}.+(json)'
},
html: {
items: products,
output: '${SRC_DIR}/../resources/${SRC_NAME}.+(html)',
input: "/**/*Global*.+(SLDASM)"
},
htmlex: {
items: [grunt.option('product')],
output: '${SRC_DIR}/../resources/${SRC_NAME}.+(html)',
input: "/**/*Global*.+(SLDASM)"
},
step: {
items: products,
output: '${SRC_DIR}/${SRC_NAME}.+(step)'
},
bom: {
items: products,
output: '${SRC_DIR}/../resources/${SRC_NAME}.+(xlsx)'
}
}
```
Where ```products``` resolves to
```js
const products = [
'products/injection/myriad-spring'
]
```
This example assumes a folder `cad` in `products/injection/myriad-spring`, with an assembly `Global.SLDASM`.
To invoke a particular task:
```sh
grunt cad-convert:html
```
Or to bypass the hardcoded 'product' array:
```sh
grunt cad-convert:htmlex --product='products/injection/myriad-spring`
```
## As batch droplet - single file
1. create a file toHTML.bat with the following content
```batch
@echo off
osr-cad sw --verbose=true --hash=false --debug=true --skip=false --src="%~1" --dst="${SRC_DIR}/${SRC_NAME}.+(html)"
```
2. drop any SW supported file onto it, to convert it to an eDrawing HTML webview
## As batch droplet - folders
1. create a file toHTMLs.bat with the following content
```batch
@echo off
osr-cad sw --verbose=true --hash=false --debug=true --skip=false --src="%~1/**/*.+(SLDASM|SLDPRT)" --dst="${SRC_DIR}/${SRC_NAME}.+(html)"
```
2. drop a folder SW supported files onto it, to convert it to an eDrawing HTML webview

View File

@ -0,0 +1,5 @@
## OSR - CAD Policies
### Assemblies
- **Valid Formats** : STEP, Solidworks, Parasolid

View File

@ -0,0 +1,399 @@
REGEDIT4
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu]
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\1]
"Item Name"="OSR-CAD"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000001
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\10]
"Item Name"="CAD -> PDF"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=true --hash=false --debug=true --skip=false --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.+(pdf)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000001
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\11]
"Item Name"="CAD -> JPG"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=true --hash=false --debug=true --skip=false --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.+(jpg)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000001
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\12]
"Item Name"="CAD -> JSON"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=true --hash=false --debug=true --skip=true --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.+(json)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\13]
"Item Name"="CAD -> Parasolid"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=true --hash=false --debug=true --skip=true --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.+(x_t)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\14]
"Item Name"="CAD -> 3MF"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=true --hash=false --debug=true --skip=true --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.+(glb)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\15]
"Item Name"="CAD - > Configs"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=true --hash=false --debug=true --skip=true --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}-configs.+(json)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\16]
"Item Name"="CAD - > All Configs"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=true --hash=false --debug=true --skip=true --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}-&{CONFIGURATION}.+(step|x_t)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000001
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\17]
"Item Name"="---"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000003
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\18]
"Item Name"="(Submenu End)"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000002
"Show In Toolbar"=dword:00000000
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\19]
"Item Name"="OSR-i18n"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000001
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\2]
"Item Name"="---"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000003
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\20]
"Item Name"="DE"
"Command"="osr-i18n"
"Arguments"="translate --alt=true --targetLang=\"DE\" --debug --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.&{DST_LANG}.+(md).md\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\21]
"Item Name"="(Submenu End)"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000002
"Show In Toolbar"=dword:00000000
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\22]
"Item Name"="OSR-L"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000001
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\23]
"Item Name"="compile"
"Command"="osrl"
"Arguments"="compile --alt=true --source=\"$(FullName)\" --output=\"&{SRC_DIR}/&{SRC_NAME}.md\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\24]
"Item Name"="(Submenu End)"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000002
"Show In Toolbar"=dword:00000000
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\25]
"Item Name"="OSR-SYNC"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000001
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\26]
"Item Name"="OSR-SYNC"
"Command"="osr-sync "
"Arguments"="sync --alt=true --clean=false --verbose=true --debug=true --source=\"$(FullName)\" --target=\"&{OSR_ROOT}/temp/$(Name)\" --profile=\"&{OSR_ROOT}/osr-commons/profiles/.osr-sync-public.json\" "
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\27]
"Item Name"="OSR-SYNC-OTHER-SIDE"
"Command"="osr-sync"
"Arguments"="sync --alt=true --clean=true --verbose=true --debug=true --source=\"$(FullName)\" --target=\"$(FullPathInactive)/$(Name)\" --profile=\"&{OSR_ROOT}/osr-commons/profiles/.osr-sync-public.json\" "
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\28]
"Item Name"="OSR-SYNC-Module"
"Command"="osr-sync"
"Arguments"="sync --alt=true --clean=true --verbose=true --debug=true --source=\"$(FullName)\" --target=\"$(FullPathInactive)/$(Name)\" --profile=\"&{OSR_ROOT}/osr-commons/profiles/.osr-sync-module.json\" "
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000001
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\29]
"Item Name"="(Submenu End)"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000002
"Show In Toolbar"=dword:00000000
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\3]
"Item Name"="DRAW-IO -> PNG"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=true --hash=false --debug=true --skip=false --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.+(png)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\30]
"Item Name"="OSR-Media"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000001
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\31]
"Item Name"="JPG-Resize-Dir-80%"
"Command"="osr-media"
"Arguments"="resize --minWidth=1900 --width=1980 --alt=true --verbose=true --debug=true --src=\"$(FullName)/**/*.+(&{IMAGES})\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000001
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\32]
"Item Name"="WEBP->JPG"
"Command"="osr-media"
"Arguments"="resize --percent=100 --alt=true --verbose=true --debug=true --src=\"$(FullName)/*.+(webp)\" --dst=\"$(FullName)/*.+(jpg)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\33]
"Item Name"="(Submenu End)"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000002
"Show In Toolbar"=dword:00000000
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\4]
"Item Name"="DRAW-IO -> PDF"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=true --hash=false --debug=true --skip=false --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.+(pdf)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000001
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\5]
"Item Name"="DRAW-IO -> JPG"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=true --hash=false --debug=true --skip=false --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.+(jpg)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\6]
"Item Name"="DRAW-IO -> SVG"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=true --hash=false --debug=true --skip=false --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.+(svg)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\7]
"Item Name"="---"
"Command"=""
"Arguments"=""
"Initial Directory"=""
"Execute Through Shell"=dword:00000000
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000000
"Icon"=""
"Type"=dword:00000003
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\8]
"Item Name"="CAD -> HTML"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=false --hash=false --debug=false --skip=true --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.+(html)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000000
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\9]
"Item Name"="CAD -> STEP"
"Command"="osr-cad"
"Arguments"="sw --alt=true --verbose=true --hash=false --debug=true --skip=false --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.+(step)\""
"Initial Directory"="$(FullPath)"
"Execute Through Shell"=dword:00000001
"Close Shell Window"=dword:00000001
"Open Shell Window"=dword:00000001
"Icon"=""
"Type"=dword:00000000
"Show In Toolbar"=dword:00000001

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

60
packages/cad/package.json Normal file
View File

@ -0,0 +1,60 @@
{
"name": "@polymech/cad",
"version": "0.8.8",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"help": "node build/main.js --help",
"build": "tsc -p .",
"dev": "tsc -w -p . --declaration",
"publish": "npm publish --access public"
},
"repository": {
"type": "git",
"url": "git+https://git.osr-plastic.org/osr-plastic/cad.git"
},
"author": "PlasticHub",
"keywords": [
"SolidWorks",
"CAD",
"STEP",
"3DXML",
"3D"
],
"bin": {
"osr-cad": "main.js"
},
"publishConfig": {
"access": "public"
},
"license": "ISC",
"bugs": {
"url": "https://git.osr-plastic.org/osr-plastic/cad/issues"
},
"homepage": "https://git.osr-plastic.org/osr-plastic/cad#readme",
"dependencies": {
"@polymech/cache": "link:..\\cache",
"@polymech/commons": "link:..\\commons",
"@polymech/core": "link:..\\core",
"@polymech/fs": "link:..\\fs",
"@types/node": "^20.17.9",
"@types/yargs": "^17.0.11",
"add": "^2.0.6",
"csv-stringify": "^5.6.1",
"dxf-parser": "^1.1.2",
"errlop": "^2.1.0",
"fast-glob": "^3.3.2",
"js-yaml": "^4.1.0",
"md5": "^2.3.0",
"p-map": "^7.0.3",
"parse-glob": "^2.0.1",
"regexp.escape": "^1.1.0",
"tslint": "^5.10.0",
"which": "^2.0.2",
"yargs": "^17.7.2"
},
"devDependencies": {
"typescript": "^5.7.3",
"@types/js-yaml": "^4.0.9",
"@types/which": "^1.3.2"
}
}

View File

@ -0,0 +1,13 @@
# explicit SW assemblies to HTML via edrawings
# node ./main sw \
# --skip=false \
# --dry=true \
# --debug=true \
# --verbose=true \
# --alt=true \
# --src='./tests/drive/*.+(SLDASM)' \
# --dst='${SRC_DIR}/${SRC_NAME}.+(html)'
sw/2022/bom.exe "C:\Users\mc007\Desktop\osr\osr-cad\tests\drive\400_Drive.SLDASM" "C:\Users\mc007\Desktop\osr\osr-cad\tests\drive\400_Drive.xlsx"

View File

@ -0,0 +1,13 @@
# explicit SW assemblies to HTML via edrawings
# node ./main sw \
# --skip=false \
# --dry=true \
# --debug=true \
# --verbose=true \
# --alt=true \
# --src='./tests/drive/*.+(SLDASM)' \
# --dst='${SRC_DIR}/${SRC_NAME}.+(html)'
sw/2022/ExportHTML.exe "C:\Users\mc007\Desktop\osr\osr-cad\tests\drive\400_Drive.SLDASM" "C:\Users\mc007\Desktop\osr\osr-cad\tests\drive\400_Drive.html"

View File

@ -0,0 +1,22 @@
const { compile } = require('nexe')
compile({
input: './main.js',
build: true, //required to use patches
clean:false,
output:'./dist/win32/osr-cad.exe',
verbose:true,
fakeArgv: false,
temp:'./.nexe',
_patches: [
async (compiler, next) => {
await compiler.setFileContentsAsync(
'lib/new-native-module.js',
'module.exports = 42'
)
return next()
}
]
}).then(() => {
console.log('success')
})

View File

@ -0,0 +1 @@
npx nexe .\main.js osr-cad.exe --build --verbose

View File

@ -0,0 +1,20 @@
# explicit SW assemblies to HTML via edrawings
# node ./main sw \
# --skip=false \
# --dry=true \
# --debug=true \
# --verbose=true \
# --alt=true \
# --src='./tests/drive/*.+(SLDASM)' \
# --dst='${SRC_DIR}/${SRC_NAME}.+(html)'
node ./main sw \
--skip=false \
--dry=false \
--debug=true \
--verbose=true \
--alt=true \
--src='./tests/drive/*.+(SLDASM)' \
--dst='&{SRC_DIR}/&{SRC_NAME}.+(xlsx)'

View File

@ -0,0 +1,19 @@
# explicit SW assemblies to HTML via edrawings
# node ./main sw \
# --skip=false \
# --dry=true \
# --debug=true \
# --verbose=true \
# --alt=true \
# --src='./tests/drive/*.+(SLDASM)' \
# --dst='${SRC_DIR}/${SRC_NAME}.+(html)'
node ./main sw \
--skip=true \
--dry=false \
--debug=true \
--verbose=true \
--alt=true \
--src='./tests/drive/*.+(SLDASM)' \
--dst='&{SRC_DIR}/&{SRC_NAME}.+(html)'

View File

@ -0,0 +1,4 @@
## Draw.io to png
node main.js sw --alt=true --verbose=true --debug=true --skip=false --src="C:\Users\mc007\Desktop\osr\system\_pages\packages.drawio" --dst="&{SRC_DIR}/&{SRC_NAME}.+(png)"
node main.js sw --alt=true --verbose=true --debug=true --skip=false --src="C:\Users\mc007\Desktop\osr\system\_pages\packages.drawio" --dst="&{SRC_DIR}/&{SRC_NAME}.+(jpg)"
node main.js sw --alt=true --verbose=true --debug=true --skip=false --src="C:\Users\mc007\Desktop\osr\system\_pages\packages.drawio" --dst="&{SRC_DIR}/&{SRC_NAME}.+(pdf)"

0
packages/cad/src/.gitignore vendored Normal file
View File

13
packages/cad/src/_cli.ts Normal file
View File

@ -0,0 +1,13 @@
// tweaks and handlers
export const defaults = () => {
// default command
const DefaultCommand = 'summary';
if (process.argv.length === 2) {
process.argv.push(DefaultCommand);
}
// currently no default handler, display only :
process.on('unhandledRejection', (reason: string) => {
console.error('Unhandled rejection, reason: ', reason);
});
};

View File

@ -0,0 +1 @@
export { convert, pack } from './sw-lib'

View File

@ -0,0 +1,384 @@
import * as fs from 'fs'
import * as path from 'path'
import * as pMap from 'p-map'
import { sync as which } from 'which'
import { resolve, OSR_CACHE } from '@plastichub/osr-commons'
import { equalFiles, swProcMessage } from './sw-util'
import { reportCSV } from '../report'
import { logger, substitute } from '..'
import { removeEmpty } from '../lib/'
import { SolidworkOptions } from '../types'
import { Helper } from '../lib/process/index'
import { sync as exists } from "@plastichub/fs/exists"
import { sync as read } from "@plastichub/fs/read"
import { sync as write } from "@plastichub/fs/write"
import { sync as dir } from "@plastichub/fs/dir"
import { sync as rm } from "@plastichub/fs/remove"
import { clone } from "@plastichub/core/objects"
import { swRayTraceRenderQuality_e, IAssembly, IAssemblyData } from './sw-types'
import { get_cached, get_path_cached, get_cache_key, set_cached } from '@plastichub/osr-cache/lib'
import {
BUF_SIZE_CMP,
MODULE_NAME,
MSG_FAILED_TO_LOAD
} from '../constants'
import { closeAppByName, fileAsBuffer, getSWBin, removeEmptyValues } from './sw-util'
export const convertFile = async (
src,
target,
view: string,
onNode: (data) => void = () => { },
options: SolidworkOptions,
configuration: string) => {
configuration = options.configuration || configuration
options.close && closeAppByName('SLDWORKS')
const osr_cache = OSR_CACHE()
let cache_key_obj: any = {
sw: options.sw,
src,
target,
configuration
}
if (target.endsWith('.jpg')) {
cache_key_obj =
{
...cache_key_obj,
quality: options.quality,
width: options.width,
height: options.height,
renderer: options.renderer
}
}
if (target.endsWith('.xlsx')) {
cache_key_obj = {
...cache_key_obj,
"bom-config": options['bom-config'],
"bom-detail": options['bom-detail'],
"bom-template": options['bom-template'],
"bom-type": options['bom-type'],
"bom-images": options['bom-images'],
}
}
const ca_options = JSON.parse(JSON.stringify(removeEmpty(cache_key_obj)))
let cached = await get_cached(src, ca_options, MODULE_NAME)
const cachedPath = await get_path_cached(src, ca_options, MODULE_NAME)
if (!exists(target)) {
cached = null;
}
if (osr_cache && cached && cachedPath && options.cache == true) {
if (!exists(target) || !equalFiles(target, cachedPath)) {
write(target, Buffer.from(cached))
}
logger.debug(`[${MODULE_NAME}] Skipping conversion of ${src} to ${target}`)
await onNode({ src, target, options })
return Promise.resolve()
}
const parts = path.parse(target)
const source_parts = path.parse(src)
let exe = '' + options.script
let cwd = getSWBin(options.sw)
let _target = '' + target
let onPost = null
// SW Photoview wont render correctly in hidden mode
if (parts.ext === '.jpg' && source_parts.ext.toLowerCase() === '.sldasm' && options.renderer.toLowerCase() === ' ') {
logger.debug(`[${MODULE_NAME}] Converting ${src} to ${target} : - Photoview: - ` + options.hidden)
options.hidden = "false"
}
let args = [
`--source="${src}"`,
`--target="${target}"`,
`--configuration="${configuration}"`,
`--view="*${view}"`,
`--hidden=` + options.hidden || "true",
`--width=` + options.width,
`--height=` + options.height,
`--swv=` + options.swv,
`--renderer=` + options.renderer.toLowerCase() || "solidworks",
`--quality=${options.quality || swRayTraceRenderQuality_e.swRenderQuality_Good}`
]
if (options.save) args.push(`--save`)
if (options.pack) args.push(`--pack`)
if (options.rebuild) args.push(`--rebuild`)
if (options.light) args.push(`--light`)
if (options.write) args.push(`--write`)
if (parts.ext === '.json' && source_parts.ext.toLowerCase() === '.sldasm') {
exe = 'model-reader.exe'
args = [
`--source="${path.resolve(src)}"`,
`--target="${_target}"`
]
onPost = () => {
try {
let props = read(_target, 'json') as any[];
if (!props) {
logger.error('Error reading model file ', src)
return false
}
props = props.map(removeEmpty)
write(_target, props)
return true
} catch (e) {
logger.error(`Error executing model-reader::onPost for ${src} to ${_target}`)
write(_target, {})
return false
}
}
}
if (parts.base.endsWith('-configs.json') && source_parts.ext.toLowerCase() === '.sldasm') {
exe = 'getconfigs.exe'
args = [
`--source="${path.resolve(src)}"`,
`--target="${path.resolve(_target)}"`
]
onPost = () => {
try {
let props = read(_target, 'json') as any[];
if (!props) {
logger.error('Error reading configurations file ', src)
return false
}
return true
} catch (e) {
logger.error(`Error executing get::onPost for ${src} to ${_target}`)
write(_target, {})
return false
}
}
}
if (parts.ext === '.html') {
exe = 'ExportHTML.exe'
if (!configuration || configuration === 'Default') {
args = [
`"${src}"`,
`"${target}"`,
]
} else if (configuration) {
//EDrawings Control doesnt support configurations directly, we need a configuration specific edrawings file exported instead
const eDrawingsFile = src.toLowerCase().replace('.sldasm', `-${configuration}.EASM`)
if (!exists(eDrawingsFile)) {
logger.error(`Configuration specific edrawing file ${eDrawingsFile} doesnt exists`)
return Promise.resolve()
}
args = [
`"${eDrawingsFile}"`,
`"${target}"`,
`${configuration}`
]
}
}
if (parts.ext === '.xlsx') {
exe = 'bom.exe';
args = [
`"${src}"`,
`"${target}"`,
`--configuration ${options['bom-config']}`,
`--type ${options['bom-type']}`,
`--detail ${options['bom-detail']}`
]
options['bom-images'] && args.push('--images')
options['bom-template'] && args.push(`--template ${options['bom-template']}`)
if (!options.cache && exists(target)) {
rm(target);
}
}
if (source_parts.ext === '.drawio') {
exe = 'draw.io.exe';
try {
cwd = path.parse(which(exe)).dir;
} catch (e) {
logger.error(`Cant find ${exe}`);
return Promise.resolve();
}
args = [
`"${src}"`,
'-x',
`-f ${parts.ext.replace('.', '')}`,
`${options.args}`
]
}
const bin = path.resolve(`${cwd}/${exe}`)
if (!exists(bin)) {
logger.error(`${bin} doesnt exists in ${cwd}`)
logger.error('__dirname:' + __dirname)
logger.error('options.sw ' + options.sw)
return
}
const ret = await Helper.run(cwd, exe, args, options.debug)
ret.messages = [...new Set(ret.messages)]
const failed = !!ret.messages.find((m: string) => m.includes(MSG_FAILED_TO_LOAD))
ret.messages = ret.messages.map((m: string) => swProcMessage(m)).filter(x => x != null).map(x => x.message)
const info = {
...ret,
src,
target,
failed: failed,
options
}
await onNode(info)
onPost && onPost()
if (info.failed) {
rm(_target)
return ret
}
osr_cache && options.cache == true && await set_cached(src, ca_options, MODULE_NAME, fileAsBuffer(_target))
options.close && closeAppByName('SLDWORKS')
return ret
}
export async function convertFiles(file, targets: string[], view, onNode: (data: any) => void = () => { }, options: SolidworkOptions) {
if (options.dry) {
logger.info(`Dry run convert ${file} to `, targets.map((t)=>{`\n\t${t}`}).join(',\n'))
return Promise.resolve()
}
return pMap(targets, (target: any) => {
return convertFile(file, target.target, view, onNode, options, target.configuration);
}, { concurrency: 1 })
}
export const report = (data, dst: string) => {
let report: any = null;
if (dst.endsWith('.md')) {
//report = reportMarkdown(data)
}
if (dst.endsWith('.csv')) {
report = reportCSV(data)
}
logger.info(`Write report to ${dst}`)
report = write(dst, data)
return report;
}
export const targets = (f: string, options: SolidworkOptions) => {
const srcParts = path.parse(f)
const variables = clone(options.variables)
const targets = []
let configurations: any = { "Default": null }
if (options.configuration && options.configuration !== 'Default') {
configurations[options.configuration] = null
delete configurations["Default"]
}
if (options.dstInfo.PATH.includes('{CONFIGURATION}') &&
srcParts.ext.toLowerCase() === '.sldasm') {
const configurationsFile = `${srcParts.dir}/${srcParts.name}-configs.json`
if (exists(configurationsFile)) {
try {
configurations = read(configurationsFile, 'json')
} catch (error) {
logger.error(`Error reading configurations file ${configurationsFile}`);
}
}
}
for (const conf in configurations) {
if (options.dstInfo.IS_GLOB) {
options.dstInfo.GLOB_EXTENSIONS.forEach((e) => {
variables.SRC_NAME = srcParts.name
variables.SRC_DIR = srcParts.dir
variables.CONFIGURATION = conf
let targetPath = substitute(options.alt, options.variables.DST_PATH, variables)
targetPath = path.resolve(targetPath.replace(options.variables.DST_FILE_EXT, '') + e)
const parts = path.parse(targetPath)
if (srcParts.ext === parts.ext) {
return
}
if (!exists(parts.dir)) {
try {
dir(parts.dir)
} catch (e) {
if (options.debug) {
logger.error(`Error creating target path ${parts.dir} for ${targetPath}`);
}
return
}
}
targets.push({
target: targetPath,
configuration: conf
})
})
} else {
variables.SRC_NAME = srcParts.name
variables.SRC_DIR = srcParts.dir
variables.CONFIGURATION = conf
let targetPath = substitute(options.alt, options.variables.DST_PATH, variables)
if (!exists(targetPath)) {
try {
dir(targetPath)
} catch (e) {
if (options.debug) {
logger.error(`Error creating target path ${targetPath}`)
}
return
}
}
targets.push({
target: targetPath,
configuration: conf
})
}
}
return targets
}
export async function convert(options: SolidworkOptions) {
logger.setSettings({ minLevel: options.logLevel as any || 'warn' })
let reports = []
const onNode = options.onNode || ((data) => reports.push(data))
if (options.srcInfo.FILES.length === 0) {
logger.warn(`No files found to convert : `, options.src)
return
}
//skip orphan / temporary files
options.srcInfo.FILES = options.srcInfo.FILES.filter((f) => {
return f.includes('~$') === false
})
const ret = await pMap(options.srcInfo.FILES, async (f) => {
const outputs = targets(f, options)
logger.info(`Convert ${f} to ${outputs.map(t => t.target).join(',')}`)
return convertFiles(f, outputs, options.view, onNode, options)
}, { concurrency: 1 })
if (options.report) {
const reportOutFile: string = path.resolve(resolve(options.report, false, {
dst: options.srcInfo.DIR,
...options.variables,
CONFIGURATION: options.configuration || ''
}))
logger.debug(`Write report to ${reportOutFile}`)
report(reports, reportOutFile)
}
return ret
}
/*
const on3DHTML = (src, dst, options: SolidworkOptions) => {
const web_root = path.resolve(__dirname + '/../../web/xeo');
const config = JSON.parse(read(path.resolve(__dirname + '/../../config.json')) as any);
const templatePath = path.resolve(`${web_root}/template.html`);
const template = read(templatePath, 'string') as string;
const srcParts = path.parse(src);
const variables = {
...config.variables,
SRC_PATH_WEB: './' + srcParts.name + '_3D.html',
MODEL_SRC: './' + srcParts.name + '.3dxml',
};
const content = substitute(false, template, variables);
write(dst, content);
}
*/

View File

@ -0,0 +1,23 @@
import * as fs from 'fs'
import * as path from 'path'
import * as pMap from 'p-map'
import { sync as which } from 'which'
import { execSync } from 'child_process'
import { reportCSV } from '../report'
import { logger, substitute } from '..'
import { removeEmpty } from '../lib/'
import { SolidworkOptions } from '../types'
import { Helper } from '../lib/process/index'
import { sync as exists } from "@plastichub/fs/exists"
import { sync as read } from "@plastichub/fs/read"
import { sync as write } from "@plastichub/fs/write"
import { sync as dir } from "@plastichub/fs/dir"
import { sync as rm } from "@plastichub/fs/remove"
import { clone } from "@plastichub/core/objects"
export * from './sw-convert'
export * from './sw-util'
export * from './sw-pack'

View File

@ -0,0 +1,62 @@
import * as path from 'path'
import * as pMap from 'p-map'
import { logger } from '..'
import { SolidworkOptions } from '../types'
import { Helper } from '../lib/process/index'
import { sync as exists } from "@plastichub/fs/exists"
import { getSWBin } from './sw-util'
export async function packFile(file, onNode: (data: any) => void = () => { }, options: SolidworkOptions) {
if (options.dry) {
return Promise.resolve();
}
const target = options.dst;
if (options.cache && exists(target)) {
onNode({
src: file,
target
});
return Promise.resolve();
}
let exe = '' + options.script;
let args = [
`"${file}"`,
`"${target}"`
]
const cwd = getSWBin(options.sw);
const bin = path.resolve(`${cwd}/${exe}`);
if (!exists(bin)) {
logger.error(`${bin} doesnt exists in ${cwd}`)
logger.error('__dirname:' + __dirname)
logger.error('options.sw ' + options.sw)
return
}
options.debug && logger.debug(`Running ${cwd}/${exe} with`, args)
const promise = Helper.run(cwd, exe, args, options.debug)
promise.then((d) => {
onNode({
...d,
src: file,
target
})
})
return promise
}
export async function pack(options: SolidworkOptions) {
let reports = []
const onNode = (data) => { reports.push(data) }
options.verbose && logger.info(`Pack ${options.srcInfo.FILES.length} files `)
const ret = await pMap(options.srcInfo.FILES, async (f) => {
logger.debug(`Convert ${f} to `, options.dst)
return packFile(f, onNode, options)
}, { concurrency: 1 })
return ret
}

View File

@ -0,0 +1,252 @@
export interface IComponent {
Name: string;
Path: string;
IsSuppressed: boolean;
}
export interface IMass {
Mass: number;
Density: number;
Volume: number;
SurfaceArea: number;
CenterOfMassX: number;
CenterOfMassY: number;
CenterOfMassZ: number;
}
export interface IBox {
MinX: number;
MinY: number;
MinZ: number;
MaxX: number;
MaxY: number;
MaxZ: number;
}
export interface IMaterial {
Material: string;
"Materials": string;
}
export interface IProperties {
[key: string]: object;
}
export interface IAssembly {
Components: IComponent[];
}
export interface ITreeNode {
Name: string
Children: Node[]
Path: string
Parent: string | null
Properties: IProperties
Equations: { [key: string]: number | string }
Mass: IMass
Box: IBox
Material: { [key: string]: string }
States: { [key: string]: object }
LaserParts: any
activeConfiguration: any
}
export interface Configuration {
"Total Bounding Box Length"?: string
"Total Bounding Box Width"?: string
"Total Bounding Box Thickness"?: string
"Total Bounding Box Volume"?: string
"Weight"?: string
"Cost - Total Cost"?: string
"IsLaser?": string
"Hide"?: string
"Catalog"?: string
"Configurations"?: string
}
export interface Configurations {
[key:string]: Configuration
}
export interface IAssemblyData {
assembly: IAssembly
root: ITreeNode
Configurations: Configurations
}
export enum swRayTraceRenderImageFormat {
swImageFormat_FlexiblePrecision,
swImageFormat_Targa,
swImageFormat_WindowsBmp,
swImageFormat_HDR,
swImageFormat_JPEG2000,
swImageFormat_JPEG2000_16bit,
swImageFormat_JPEG2000_16bit_Lossless,
swImageFormat_JPEG,
swImageFormat_PNG,
swImageFormat_PNG_16bit,
swImageFormat_SGI_RGB,
swImageFormat_TIF,
swImageFormat_TIF_16bit,
swImageFormat_TIF_16bit_uncompr,
swImageFormat_OpenEXR,
swImageFormat_OpenEXR_32bit,
swImageFormat_OpenEXR_TILED16bit,
swImageFormat_OpenEXR_TILED32bit
}
export enum swRayTraceRenderQuality_e {
swRenderQuality_Good,
swRenderQuality_Better,
swRenderQuality_Best,
swRenderQuality_Maximum
}
export enum ImageAspectRatio {
AspectRatio_1_1 = "1:1",
AspectRatio_4_3 = "4:3",
AspectRatio_16_9 = "16:9",
AspectRatio_21_9 = "21:9",
AspectRatio_2_3 = "2:3",
AspectRatio_3_2 = "3:2",
AspectRatio_5_4 = "5:4",
AspectRatio_3_4 = "3:4",
AspectRatio_9_16 = "9:16",
AspectRatio_10_16 = "10:16",
AspectRatio_16_10 = "16:10",
AspectRatio_16_15 = "16:15",
AspectRatio_18_9 = "18:9",
AspectRatio_32_9 = "32:9",
AspectRatio_48_9 = "48:9",
}
export enum ImageResolution {
Resolution_640x480 = "640x480",
Resolution_800x600 = "800x600",
Resolution_1024x768 = "1024x768",
Resolution_1280x720 = "1280x720",
Resolution_1280x800 = "1280x800",
Resolution_1280x1024 = "1280x1024",
Resolution_1366x768 = "1366x768",
Resolution_1440x900 = "1440x900",
Resolution_1600x900 = "1600x900",
Resolution_1680x1050 = "1680x1050",
Resolution_1920x1080 = "1920x1080",
Resolution_1920x1200 = "1920x1200",
Resolution_2560x1440 = "2560x1440",
Resolution_2560x1600 = "2560x1600",
Resolution_3840x2160 = "3840x2160",
Resolution_4096x2160 = "4096x2160",
Resolution_5120x2880 = "5120x2880",
Resolution_7680x4320 = "7680x4320",
}
export interface IRayTraceRendererOptions {
imageWidth: number;
imageHeight: number;
imageFormat: number;
previewRenderQuality: number;
finalRenderQuality: number;
bloomEnabled: boolean;
bloomThreshold: number;
bloomRadius: number;
contourEnabled: boolean;
shadedContour: boolean;
contourLineThickness: number;
contourLineColor: number;
useSolidWorksViewAspectRatio: boolean;
defaultImagePath: string;
useSceneBackgroundImageAspectRatio: boolean;
outputAmbientOcclusion: boolean;
directCaustics: boolean;
causticQuality: number;
gamma: number;
numberOfReflections: number;
numberOfRefractions: number;
networkRendering: boolean;
clientWorkload: number;
sendDataForNetworkJob: boolean;
networkSharedDirectory: string;
causticAmount: number;
customRenderSettings: boolean;
contourCartoonRenderingEnabled: boolean;
hasCartoonEdges: boolean;
hasCartoonShading: boolean;
includeAnnotationsInRendering: boolean;
renderType: number;
renderAnnotationsToSeparateImage: boolean;
alphaOutput: boolean;
offloadedRendering: boolean;
}
function createRendererOptions(options?: Partial<IRayTraceRendererOptions>): IRayTraceRendererOptions {
const defaults: IRayTraceRendererOptions = {
imageWidth: 800,
imageHeight: 600,
imageFormat: 0,
previewRenderQuality: 50,
finalRenderQuality: 100,
bloomEnabled: true,
bloomThreshold: 0.5,
bloomRadius: 10,
contourEnabled: false,
shadedContour: false,
contourLineThickness: 1,
contourLineColor: 0,
useSolidWorksViewAspectRatio: false,
defaultImagePath: '',
useSceneBackgroundImageAspectRatio: false,
outputAmbientOcclusion: false,
directCaustics: false,
causticQuality: 50,
gamma: 2.2,
numberOfReflections: 2,
numberOfRefractions: 2,
networkRendering: false,
clientWorkload: 0,
sendDataForNetworkJob: false,
networkSharedDirectory: '',
causticAmount: 0.5,
customRenderSettings: false,
contourCartoonRenderingEnabled: false,
hasCartoonEdges: false,
hasCartoonShading: false,
includeAnnotationsInRendering: false,
renderType: 0,
renderAnnotationsToSeparateImage: false,
alphaOutput: false,
offloadedRendering: false,
};
return { ...defaults, ...options };
}
export enum EDocumentState {
//
// Summary:
// Default state of the document
Default = 0,
//
// Summary:
// Checks if document is hidden
Hidden = 1,
//
// Summary:
// Opens document in read-only mode
ReadOnly = 2,
//
// Summary:
// Opens document in view only mode
ViewOnly = 4,
//
// Summary:
// Opens document without displaying any popup messages
Silent = 8,
//
// Summary:
// Opens document in the rapid mode
//
// Remarks:
// This mode significantly improves the performance of opening but certain functionality
// and API migth not be available
Rapid = 0x10
}

View File

@ -0,0 +1,87 @@
import * as fs from 'fs'
import * as path from 'path'
import { execSync } from 'child_process'
import { sync as exists } from "@plastichub/fs/exists"
import { sync as read } from "@plastichub/fs/read"
import { BUF_SIZE_CMP } from '../constants'
export const swProcMessage = (log: string):{ logLevel: string, message: string } | null => {
const regex = /<<(\w+)::(.*?)>>/
const match = log.match(regex)
if (match) {
return {
logLevel: match[1],
message: match[2]
}
}
}
export const fileAsBuffer = (path: string) => read(path, 'buffer') as Buffer || Buffer.from("-")
export const getSWBin = (argv: string) => {
const swVersion = parseInt(argv)
if (swVersion) {
return path.resolve(__dirname + `/../sw/${swVersion}`)
} else {
return path.resolve(argv)
}
}
export function closeAppByName(appName: string): void {
try {
const command = `tasklist /FI "IMAGENAME eq ${appName}.exe" /NH`;
const output = execSync(command).toString();
const lines = output.split('\n');
const processIdLine = lines.find(line => line.includes(appName));
if (!processIdLine) {
return;
}
const processId = parseInt(processIdLine.split(/\s+/)[1], 10);
execSync(`taskkill /F /PID ${processId}`);
} catch (error) {}
}
export function removeEmptyValues(obj: any): any {
for (const key in obj) {
const value = obj[key];
if (!value || typeof value !== 'number' ||
typeof value !== 'boolean' ||
typeof value !== 'string') {
delete obj[key];
}
}
return obj
}
export const equalFiles = (pathA, pathB) => {
if (!exists(pathA) || !exists(pathB)) {
return false
}
let statA = fs.lstatSync(pathA)
let statB = fs.lstatSync(pathB)
if (statA.size !== statB.size) {
return false
};
let fdA = fs.openSync(pathA, 'r')
let fdB = fs.openSync(pathB, 'r')
let bufA = Buffer.alloc(BUF_SIZE_CMP)
let bufB = Buffer.alloc(BUF_SIZE_CMP)
let readA = 1
let readB = 1
while (readA > 0) {
readA = fs.readSync(fdA, bufA, 0, bufA.length, null)
readB = fs.readSync(fdB, bufB, 0, bufB.length, null)
if (readA !== readB) {
return false
}
for (let i = 0; i < readA; i++) {
if (bufA[i] !== bufB[i]) {
return false
}
}
}
fs.closeSync(fdA)
fs.closeSync(fdB)
return true
}

View File

View File

@ -0,0 +1,26 @@
import * as CLI from 'yargs'
import { CONFIG_DEFAULT, DEFAULT_ROOTS } from '@plastichub/osr-commons';
import { logger } from '../'
const defaultOptions = (yargs: CLI.Argv) => {
return yargs.option('debug', {
default: 'false',
describe: 'debug messages'
}).option('env_key', {
default: 'OSR-CONFIG',
describe: 'Environment key to the config path'
})
}
let options = (yargs: CLI.Argv) => defaultOptions(yargs);
export const register = (cli: CLI.Argv) => {
return cli.command('info', 'info', options, async (argv: CLI.Arguments) => {
if (argv.help) { return }
const args: any = argv
const src = CONFIG_DEFAULT(args.env_key)
logger.debug(`Reading OSR Config with key "${argv.env_key}"`, src)
logger.debug(`OSR Paths:`, DEFAULT_ROOTS)
})
}

View File

@ -0,0 +1,54 @@
import * as CLI from 'yargs'
import { logger } from '../'
import { SolidworkOptions } from '../types'
import { sanitizeSingle } from '../sw_argv'
import { pack } from '../cad/index'
export const defaultOptions = (yargs: CLI.Argv) => {
return yargs.option('src', {
default: './',
describe: 'The source directory or source file. Glob patters are supported!',
demandOption: true
}).option('dst', {
describe: 'Destination folder or file'
}).option('view', {
default: 'Isometric',
describe: 'Sets the target view'
}).option('Report', {
describe: 'Optional conversion report. Can be JSON, HTML, CSV or Markdown'
}).option('debug', {
default: false,
describe: 'Enable internal debug messages',
type: 'boolean'
}).option('skip', {
default: true,
describe: 'Skip existing files',
type: 'boolean',
}).option('dry', {
default: false,
describe: 'Run without conversion but create reports',
type: 'boolean'
}).option('alt', {
default: false,
describe: 'Alternate tokenizer'
}).option('verbose', {
default: true,
describe: 'Show internal messages',
type: 'boolean'
}).option('sw', {
describe: 'Set explicit the path to the Solidworks binaries & scripts.\
"It assumes SolidWorks.Interop.sldworks.dll and export.cmd at this location!'
}).option('script', {
describe: 'Set explicit the path to the Solidworks script'
})
}
let options = (yargs: CLI.Argv) => defaultOptions(yargs)
export const register = (cli: CLI.Argv) => {
return cli.command('pack', '', options, async (argv: CLI.Arguments) => {
if (argv.help) { return }
const options = sanitizeSingle(argv) as SolidworkOptions
logger.setSettings({ minLevel: options.logLevel as any})
logger.debug("options " + argv.dst, options)
return pack(options) as any
})
}

View File

@ -0,0 +1,70 @@
import * as CLI from 'yargs'
import * as path from 'path'
import { logger } from '..'
import { SlicerOptions } from '../types'
import { defaultOptions, sanitize } from '../slic3r_argv'
import { convert } from '../print'
import { load } from 'js-yaml'
import { clone } from "@plastichub/core/objects"
import { sync as read } from "@plastichub/fs/read"
const SLIC3R_DEFAULTS = () => path.resolve(path.join(__dirname, '../profiles/slic3r_defaults.yaml'))
const defaults = (defaults, options) => {
let key,
returnObject
returnObject = clone(options) || {}
for (key in defaults)
if (defaults.hasOwnProperty(key) &&
typeof returnObject[key] === 'undefined')
returnObject[key] = defaults[key]
return returnObject
}
export const register = (cli: CLI.Argv) => {
const defaults_path = SLIC3R_DEFAULTS()
cli.parserConfiguration({
"short-option-groups": true,
"camel-case-expansion": false
})
const defaultsRaw = read(defaults_path) as string
let defaults_json: any = load(defaultsRaw)
let options: any = (yargs: CLI.Argv) => {
let opts = defaultOptions(yargs)
Object.keys(defaults_json.properties).forEach((k) => {
const val = defaults_json.properties[k]
switch (defaults_json.properties[k].type) {
case 'object':
case 'boolean':
case 'number':
case 'string': {
opts = opts.option(k, val)
break;
}
}
})
return opts
}
return cli.command('slice', 'Run Slic3r', options, async (argv: CLI.Arguments) => {
if (argv.help) { return }
let options = sanitize(argv) as SlicerOptions
options = defaults(defaults_json, options)
options.debug && logger.info("options " + argv.dst, options)
return convert(options)
})
}

View File

@ -0,0 +1,18 @@
import * as CLI from 'yargs'
import { logger } from '..'
import { SolidworkOptions } from '../types'
import { defaultOptions, sanitize } from '../sw_argv'
import { convert } from '../cad/index'
let options = (yargs: CLI.Argv) => defaultOptions(yargs)
export const register = (cli: CLI.Argv) => {
return cli.command('sw-pack', 'Pack and Go - Using the Zip option to preserve folder structure', options, async (argv: CLI.Arguments) => {
if (argv.help) { return }
const options = sanitize(argv) as SolidworkOptions
options.debug && logger.info("options " + argv.dst, options)
return convert(options)
})
}

View File

@ -0,0 +1,17 @@
import * as CLI from 'yargs'
import { logger } from '..'
import { SolidworkOptions } from '../types'
import { defaultOptions, sanitize } from '../sw_argv'
import { convert } from '../cad'
let options = (yargs: CLI.Argv) => defaultOptions(yargs)
export const register = (cli: CLI.Argv) => {
return cli.command('sw', 'Convert CAD files via Solidworks Interop API', options, async (argv: CLI.Arguments) => {
if (argv.help) { return }
const options = sanitize(argv) as SolidworkOptions
logger.setSettings({ minLevel: options.logLevel as any })
logger.info("options " + argv.dst, options)
return convert(options) as any
})
}

View File

@ -0,0 +1,7 @@
export const GIT_CHANGELOG_MESSAGE_PREFIX = 'ChangeLog:'
export const GIT_REPO = 'https://git.osr-plastic.org/osr-plastic/'
export const MODULE_NAME = `OSR-CAD`
export const PACKAGE_NAME = 'osr-cad'
export const DEFAULT_REPORT = '${SRC_DIR}/cad-report-${CONFIGURATION}.json'
export const BUF_SIZE_CMP = 16 * 1024
export const MSG_FAILED_TO_LOAD = 'Failed to load'

14
packages/cad/src/index.ts Normal file
View File

@ -0,0 +1,14 @@
export * from './log'
export * from './types'
export * from './cad/sw-lib'
export * from './_cli'
export * from './sw_argv'
export * from './lib/geometry/dxf'
import { logger as _logger } from '@plastichub/core/debug'
import { MODULE_NAME } from './constants'
export const logger = _logger(MODULE_NAME)
import { substitute as _substitute , substituteAlt as _substituteAlt } from "@plastichub/core/strings"
import { IObjectLiteral } from "@plastichub/core"
export const substitute = (alt:boolean, template:string, vars:IObjectLiteral) => alt ? _substituteAlt(template,vars) : _substitute(template, vars)

View File

@ -0,0 +1 @@
export const get = (str:string, dst:string ) => {}

View File

@ -0,0 +1,18 @@
export const sizeToString = (bytes: number, si: boolean = true) => {
var units;
var u;
var b = bytes;
var thresh = si ? 1000 : 1024;
if (Math.abs(b) < thresh) {
return b + ' B';
}
units = si
? ['kB', 'MB', 'GB', 'TB']
: ['KiB', 'MiB', 'GiB', 'TiB'];
u = -1;
do {
b /= thresh;
++u;
} while (Math.abs(b) >= thresh && u < units.length - 1);
return b.toFixed(1) + ' ' + units[u];
};

View File

@ -0,0 +1,68 @@
import * as fs from 'fs';
const dxf = require('dxf-parser')
import { sync as read } from '@plastichub/fs/read'
import { sync as write } from '@plastichub/fs/write'
interface DxfEntity {
type: string;
vertices?: { x: number; y: number }[];
start?: { x: number; y: number };
end?: { x: number; y: number };
radius?: number;
center?: { x: number; y: number };
startAngle?: number;
endAngle?: number;
}
function distanceBetweenPoints(p1: { x: number; y: number }, p2: { x: number; y: number }): number {
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}
function arcLength(radius: number, startAngle: number, endAngle: number): number {
return Math.abs(endAngle - startAngle) * radius;
}
function calculateEntityLength(entity: DxfEntity): number {
switch (entity.type) {
//case 'LINE':
// return distanceBetweenPoints(entity.start!, entity.end!);
case 'LWPOLYLINE':
case 'LINE':
let length = 0;
for (let i = 0; i < entity.vertices!.length - 1; i++) {
try {
length += distanceBetweenPoints(entity.vertices![i], entity.vertices![i + 1]);
} catch (e) {
console.log('error', entity, e)
}
}
return length;
case 'CIRCLE':
return 2 * Math.PI * entity.radius!;
case 'ARC':
return arcLength(entity.radius!, entity.startAngle!, entity.endAngle!);
default:
return 0;
}
}
function calculateTotalDxfEntitiesLength(filePath: string): number {
const parser = new dxf();
const dxfData = parser.parseSync(fs.readFileSync(filePath, 'utf-8'));
const ret = dxfData.entities.reduce((totalLength: number, entity: DxfEntity) => {
console.log('entity', entity)
const length = calculateEntityLength(entity);
return totalLength + length;
}, 0);
write( filePath + '.json', JSON.stringify(dxfData, null, 2))
return ret;
}
const test = () => {
// Usage example
const filePath = './tests/dxf/square-200-bore.DXF'
const totalLength = calculateTotalDxfEntitiesLength(filePath);
console.log(`Total length of all entities: ${totalLength}`);
}

View File

@ -0,0 +1,14 @@
export const removeEmpty = (data) => {
//transform properties into key-values pairs and filter all the empty-values
const entries = Object.entries(data).filter(([, value]) => value != null);
//map through all the remaining properties and check if the value is an object.
//if value is object, use recursion to remove empty properties
const clean = entries.map(([key, v]) => {
const value = typeof v == 'object' ? removeEmpty(v) : v
return [key, value]
})
//transform the key-value pairs back to an object.
return Object.fromEntries(clean)
}

View File

@ -0,0 +1,140 @@
import { logger } from '../../index'
import * as stream from 'stream';
import { ChildProcess, exec } from 'child_process';
export enum STATUS {
OK,
ERROR,
PENDING
}
const fatalHandler = (message: string, fn: (msg: string) => void): boolean => {
if (message.startsWith('fatal:')) {
fn('\t\ ' + message)
return true;
}
return false
}
// tslint:disable-next-line:no-empty
const subscribe = (signal: stream.Readable, collector: (data: any) => void = () => { }, options: any = {}) => {
const buffer: string[] = []
signal.on('message', (message) => logger.debug('message', message))
signal.on('error', (error) => logger.error('std-error', error))
signal.on('data', (data) => {
const message = data.toString()
buffer.push(message) // .replace(/[\x00-\x1F\x7F-\x9F]/g, "")
collector(buffer)
logger.debug("\n Process : \n\t", data)
});
};
const merge = (buffer: string[], data: any): string[] => buffer.concat(data);
const hook = (process: ChildProcess, resolve: any, reject: any, cmd: string, options: any = {}) => {
let buffer: string[] = [];
const collector = (data: any) => { buffer = buffer.concat(data); };
subscribe(process.stdout, collector, options);
subscribe(process.stderr, collector, options);
process.on('exit', (code, signal) => {
if (code) {
resolve({
code: STATUS.ERROR,
command: cmd,
error: code,
messages: buffer
})
} else {
resolve({
code: STATUS.OK,
command: cmd,
messages: buffer
})
}
})
return process
}
export class Process {
public binary = 'magick';
public cwd: string = '';
public args: string = '';
constructor(options: any = {}) {
this.binary = options.binary || this.binary;
this.cwd = options.cwd || process.cwd();
}
public optionsToString(options: any): string {
const args: any[] = [];
// tslint:disable-next-line:forin
for (const k in options) {
const val = options[k];
if (k.length === 1) {
// val is true, add '-k'
if (val === true) {
args.push('-' + k);
} else if (val !== false) {
// if val is not false, add '-k val'
args.push('-' + k + ' ' + val);
}
} else {
if (val === true) {
args.push('--' + k);
} else if (val !== false) {
args.push('--' + k + '=' + val);
}
}
}
return args.join(' ');
}
public optionsToArray(options: any): string[] {
const args: any[] = []
// tslint:disable-next-line:forin
for (const k in options) {
const val = options[k]
if (k.length === 1) {
// val is true, add '-k'
if (val === true) {
args.push('-' + k)
} else if (val !== false) {
// if val is not false, add '-k val'
args.push('-' + k + ' ' + val)
}
} else {
if (val === true) {
args.push('--' + k);
} else if (val !== false) {
args.push('--' + k + '=' + val)
}
}
}
return args
}
public async exec(command: string, options: any = {}, args: any[] = []): Promise<any> {
args = [command].concat(args)
return new Promise<any>((resolve, reject) => {
const p = exec(this.binary + ' ' + args.join(' '), {
cwd: this.cwd
})
return hook(p, resolve, reject, this.binary + ' ' + args.join(' '), options)
})
}
}
export class Helper {
public static async run(cwd, command: string, args: string[], debug_stream: boolean = false): Promise<any> {
logger.trace(`Run ${command} in ${cwd} ${args.join(' ')}`)
const gitProcess = new Process({
cwd: cwd,
binary: command
})
const p = gitProcess.exec('', {
debug: debug_stream
}, args)
if (!debug_stream) {
p.catch((e) => logger.error('Error git command : ' + command, e))
} else {
logger.trace(command + ' ' + args.join(' '))
}
return p
}
}

1
packages/cad/src/log.ts Normal file
View File

@ -0,0 +1 @@
export const noop = () => {}

18
packages/cad/src/main.ts Normal file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env node
import { defaults } from './_cli'; defaults()
import * as cli from 'yargs'
import { register as registerSW } from './commands/sw'; registerSW(cli as any)
import { register as registerSlic3r } from './commands/slice'; registerSlic3r(cli as any)
import { register as registerPack } from './commands/pack'; registerPack(cli as any)
import { register as registerInfo } from './commands/info'; registerInfo(cli as any)
const argv: any = cli.argv
if (argv.h || argv.help) {
cli.showHelp()
process.exit()
} else if (argv.v || argv.version) {
process.exit()
}

View File

@ -0,0 +1,106 @@
const Registry = require('rage-edit').Registry;
const SOFTWARE_CLASSES = 'HKCU\\Software\\Classes\\';
export const registerCommand = async options => {
if (!options) throw new Error('options are empty');
const { name, icon, command, menu } = options;
if (!name) throw new Error('name is not specified');
if (!command) throw new Error('command is not specified');
if (!menu) throw new Error('menuName is not specified');
try {
await Registry.set(`${SOFTWARE_CLASSES}*\\shell\\${name}`);
await Registry.set(`${SOFTWARE_CLASSES}*\\shell\\${name}`, '', menu);
if (icon) await Registry.set(`${SOFTWARE_CLASSES}*\\shell\\${name}`, 'Icon', (icon.endsWith('.exe') ? `${icon},0` : icon));
await Registry.set(`${SOFTWARE_CLASSES}*\\shell\\${name}\\command`, '', `"${command}" "%1"`);
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve();
};
export const registerDirectoryCommand = async options => {
if (!options) throw new Error('options are empty');
const { name, icon, command, menu } = options;
if (!name) throw new Error('name is not specified');
if (!command) throw new Error('command is not specified');
if (!menu) throw new Error('menu is not specified');
try {
await Registry.set(`${SOFTWARE_CLASSES}Directory\\shell\\${name}`);
await Registry.set(`${SOFTWARE_CLASSES}Directory\\shell\\${name}`, '', menu);
if (icon) await Registry.set(`${SOFTWARE_CLASSES}Directory\\shell\\${name}`, 'Icon', (icon.endsWith('.exe') ? `${icon},0` : icon));
await Registry.set(`${SOFTWARE_CLASSES}Directory\\shell\\${name}\\command`, '', `"${command}" "%1"`);
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve();
};
export const registerOpenWithCommand = async (extensions, options) => {
if (!extensions || !extensions.length) throw new Error('extensions is not specified');
if (!options) throw new Error('options are empty');
const { name, command } = options;
if (!name) throw new Error('name is not specified');
if (!command) throw new Error('command is not specified');
try {
await Promise.all((await findExtensionNames(extensions)).map(async n => {
await Registry.set(`${SOFTWARE_CLASSES}${n}`);
await Registry.set(`${SOFTWARE_CLASSES}${n}\\shell\\${name}`);
await Registry.set(`${SOFTWARE_CLASSES}${n}\\shell\\${name}\\command`, '', `"${command}" "%1"`);
}));
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve();
};
export const removeCommand = async name => {
if (!name) throw new Error('name is not specified');
try {
await Registry.delete(`${SOFTWARE_CLASSES}*\\shell\\${name}`);
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve();
};
export const removeDirectoryCommand = async name => {
if (!name) throw new Error('name is not specified');
try {
await Registry.delete(`${SOFTWARE_CLASSES}Directory\\shell\\${name}`);
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve();
};
export const removeOpenWithCommand = async (extensions, name) => {
if (!extensions) throw new Error('extensions is not specified');
if (!name) throw new Error('name is not specified');
try {
await Promise.all((await findExtensionNames(extensions)).map(async n => await Registry.delete(`${SOFTWARE_CLASSES}${n}\\shell\\${name}`)));
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve();
};
async function findExtensionNames (exts) {
const { ses_root } = await Registry.get('HKCR');
return Promise.all(Object.keys(ses_root).filter(e => exts.includes(e)).map(async k => (await Registry.get(`HKCR\\${k}`)).$values['']));
}

View File

@ -0,0 +1 @@
export { convert } from './slic3r'

View File

@ -0,0 +1,404 @@
import * as path from 'path'
import { logger, substitute } from '..'
import { removeEmpty } from '../lib'
import { OSR_CACHE } from '@plastichub/osr-commons'
import { sync as exists } from "@plastichub/fs/exists"
import { sync as read } from "@plastichub/fs/read"
import { sync as write } from "@plastichub/fs/write"
import { sync as dir } from "@plastichub/fs/dir"
import { Promise as BPromise } from 'bluebird'
import { sync as which } from 'which'
import * as md5 from 'md5'
import { SlicerOptions } from '../types'
import { Helper } from '../lib/process/index'
import { reportCSV } from '../report'
import {
get_cached,
set_cached
} from '@plastichub/osr-cache/lib'
import {
MODULE_NAME
} from '../constants'
export const fileAsBuffer = (path: string) => read(path, 'buffer') as Buffer || Buffer.from("-")
const SLIC3R_EXE = 'Slic3r-console.exe'
const clone = (obj) => {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
export const getSlicrDir = (options: SlicerOptions) => {
const dir = path.parse(which(SLIC3R_EXE)).dir
if (exists(dir)) {
return dir
}
}
export const getBin = (options: SlicerOptions) => {
const dir = getSlicrDir(options)
const exe = path.resolve(path.join(dir, SLIC3R_EXE))
if (exists(exe)) {
return exe
}
}
const toPercentString = (aNumber) => String(aNumber * 100) + '%'
export const convertFile = async (file, target, onNode: (data: any) => void = () => { }, options: SlicerOptions) => {
let bin = getBin(options)
if (!bin) {
logger.error(`Cant find Slicr. Please register the directory in your $PATH environment variable!`)
return
}
const getArgs = (o: SlicerOptions) => {
return [
`--output "${target}"`,
// Non-slicing actions
o.repair ? '--repair' : '',
o.cut ? '--cut' : '',
o.split ? '--split' : '',
o.info ? '--info' : '',
'--threads ' + o.threads,
// Output options
o.outputFilenameFormat ?
'--output-filename-format ' + o.outputFilenameFormat : '',
o.postProcess ? o.postProcessScripts.map(function (script) {
return '--post-process ' + script
}).join(' ') : '',
o.exportSvg ? '--export-svg' : '',
o.merge ? '--merge' : '',
// Printer options
'--nozzle-diameter ' + o.nozzleDiameter,
'--print-center ' + o.printCenter.x + ',' + o.printCenter.y,
'--z-offset ' + o.zOffset,
'--gcode-flavor ' + o.GCodeFlavor,
o.useRelativeEDistances ? '--use-relative-e-distances ' : '',
o.useFirmwareRetraction ? '--use-firmware-retraction ' : '',
o.useVolumetricE ? '--use-volumetric-e ' : '',
o.GCodeArcs ? '--gcode-arcs' : '',
o.g0 ? '--g0' : '',
o.GCodeComments ? '--gcode-comments' : '',
'--vibration-limit ' + o.vibrationLimit,
//'--pressure-advance ' + o.pressureAdvance,
// Filament options
'--filament-diameter ' + o.filamentDiameter,
'--extrusion-multiplier ' + o.extrusionMultiplier,
'--temperature ' + o.temperature,
'--first-layer-temperature ' + o.firstLayerTemperature,
'--bed-temperature ' + o.bedTemperature,
'--first-layer-bed-temperature ' + o.firstLayerBedTemperature,
// Speed options
'--travel-speed ' + o.travelSpeed,
'--perimeter-speed ' + o.perimeterSpeed,
'--small-perimeter-speed ' + o.smallPerimeterSpeed,
'--external-perimeter-speed ' + o.externalPerimeterSpeed,
'--infill-speed ' + o.infillSpeed,
'--solid-infill-speed ' + o.solidInfillSpeed,
'--top-solid-infill-speed ' + o.topSolidInfillSpeed,
'--support-material-speed ' + o.supportMaterialSpeed,
'--support-material-interface-speed ' + o.supportMaterialInterfaceSpeed,
'--bridge-speed ' + o.bridgeSpeed,
'--gap-fill-speed ' + o.gapFillSpeed,
'--first-layer-speed ' + o.firstLayerSpeed,
// Accelerator options
'--perimeter-acceleration ' + o.perimeterAcceleration,
'--infill-acceleration ' + o.infillAcceleration,
'--bridge-acceleration ' + o.bridgeAcceleration,
'--first-layer-acceleration ' + o.firstLayerAcceleration,
'--default-acceleration ' + o.defaultAcceleration,
// Accuracy options
'--layer-height ' + o.layerHeight,
'--first-layer-height ' + o.firstLayerHeight,
'--infill-every-layers ' + o.infillEveryLayers,
'--solid-infill-every-layers ' + o.solidInfillEveryLayers,
// Print Options
'--perimeters ' + o.perimeters,
'--top-solid-layers ' + o.topSolidLayers,
'--bottom-solid-layers ' + o.bottomSolidLayers,
o.solidLayers ? '--solid-layers' : '',
'--fill-density ' + toPercentString(o.fillDensity),
'--fill-angle ' + o.fillAngle,
'--fill-pattern ' + o.fillPattern,
'--solid-fill-pattern ' + o.solidFillPattern,
o.startGcode ? '--start-gcode' : '',
o.endGcode ? '--end-gcode ' : '',
o.layerGcode ? '--layer-gcode ' : '',
o.toolchangeGcode ? '--toolchange-gcode ' : '',
'--seam-position ' + o.seamPosition,
o.externalPerimetersFirst ? '--external-perimeters-first ' : '',
o.spiralVase ? '--spiral-vase ' : '',
o.onlyRetractWhenCrossingPerimeters ?
'--only-retract-when-crossing-perimeters ' : '',
'--solid-infill-below-area ' + o.solidInfillBelowArea,
o.infillOnlyWhereNeeded ? '--infill-only-where-needed ' : '',
o.infillFirst ? '--infill-first ' : '',
// Quality options
o.extraPerimeters ? '--extra-perimeters' : '',
o.avoidCrossingPerimeters ? '--avoid-crossing-perimeters' : '',
o.thinWalls ? '--thin-walls' : '',
o.overhangs ? '--overhangs' : '',
// Support material options
o.supportMaterial ? '--support-material ' : '',
'--support-material-threshold ' + o.supportMaterialThreshold,
'--support-material-pattern ' + o.supportMaterialPattern,
'--support-material-spacing ' + o.supportMaterialSpacing,
'--support-material-angle ' + o.supportMaterialAngle,
'--support-material-interface-layers ' +
o.supportMaterialInterfaceLayers,
'--support-material-interface-spacing ' +
o.supportMaterialInterfaceSpacing,
'--raft-layers ' + o.raftLayers,
'--support-material-enforce-layers ' + o.supportMaterialEnforceLayers,
o.dontSupportBridges ? '--dont-support-bridges ' : '',
// Retraction options
'--retract-length ' + o.retractLength,
'--retract-speed ' + o.retractSpeed,
'--retract-restart-extra ' + o.retractRestartExtra,
'--retract-before-travel ' + o.retractBeforeTravel,
'--retract-lift ' + o.retractLift,
o.retractLayerChange ? '--retract-layer-change' : '',
o.wipe ? '--wipe' : '',
// Retraction options for multi-extruder setups
'--retract-length-toolchange ' + o.retractLengthToolchange,
'--retract-restart-extra-toolchange ' + o.retractRestartExtraToolchange,
// Cooling options
o.cooling ? '--cooling ' : ' ',
'--min-fan-speed ' + (o.minFanSpeed * 100), // in %
'--max-fan-speed ' + (o.maxFanSpeed * 100), // in %
'--bridge-fan-speed ' + (o.bridgeFanSpeed * 100), // in %
'--fan-below-layer-time ' + o.fanBelowLayerTime,
'--slowdown-below-layer-time ' + o.slowdownBelowLayerTime,
'--min-print-speed ' + o.minPrintSpeed,
'--disable-fan-first-layers ' + o.disableFanFirstLayers,
o.fanAlwaysOn ? '--fan-always-on ' : '',
// Skirt options
'--skirts ' + o.skirts,
'--skirt-distance ' + o.skirtDistance,
'--skirt-height ' + o.skirtHeight,
'--min-skirt-length ' + o.minSkirtLength,
'--brim-width ' + o.brimWidth,
// Transform options
'--scale ' + o.scale,
'--rotate ' + o.rotate,
'--duplicate ' + o.duplicate,
'--duplicate-grid ' + o.duplicateGrid,
'--duplicate-distance ' + o.duplicateDistance,
//'--xy-size-compensation ' + o.xySizeCompensation,
// Sequential printing options
o.completeObjects ? '--complete-objects ' : '',
'--extruder-clearance-radius ' + o.extruderClearanceRadius,
'--extruder-clearance-height ' + o.extruderClearanceHeight,
// Miscellaneous options:
o.notes ? '--notes ' + o.notes : '',
'--resolution ' + o.resolution,
// '--bed-size ' + o.bedSize.width + ',' + o.bedSize.height,
// Flow options (advanced):
'--extrusion-width ' + o.extrusionWidth,
o.firstLayerExtrusionWidth ?
'--first-layer-extrusion-width ' + o.firstLayerExtrusionWidth : '',
'--perimeter-extrusion-width ' + o.perimeterExtrusionWidth,
//'--external-perimeter-extrusion-width ' +
// o.externalPerimeterExtrusionWidth,
'--infill-extrusion-width ' + o.infillExtrusionWidth,
'--solid-infill-extrusion-width ' + o.solidInfillExtrusionWidth,
'--top-infill-extrusion-width ' + o.topInfillExtrusionWidth,
'--support-material-extrusion-width ' + o.supportMaterialExtrusionWidth,
'--bridge-flow-ratio ' + o.bridgeFlowRatio,
// Multiple extruder options:
o.extruderOffset.x || o.extruderOffset.y ?
'--extruder-offset ' + o.extruderOffset.x + 'x' +
o.extruderOffset.y : '',
'--perimeter-extruder ' + o.perimeterExtruder,
'--infill-extruder ' + o.infillExtruder,
//'--solid-infill-extruder ' + o.solidInfillExtruder,
'--support-material-extruder ' + o.supportMaterialExtruder,
'--support-material-interface-extruder ' +
o.supportMaterialInterfaceExtruder,
o.oozePrevention ? '--ooze-prevention ' : '',
'--standby-temperature-delta ' + o.standbyTemperatureDelta,
`"${file}"`
]
}
const osr_cache = OSR_CACHE()
const ca_options = JSON.parse(JSON.stringify({ ...options, target, skip: null }))
const cached = await get_cached(file, ca_options, MODULE_NAME)
if (osr_cache && cached && options.cache !== false) {
let md5Src = md5(Buffer.from(cached))
let md5Dst = md5(fileAsBuffer(target))
if (!exists(target) || md5Src !== md5Dst) {
write(target, Buffer.from(cached))
}
onNode({
src: file,
target
})
return Promise.resolve()
}
let _target = '' + target
let args = getArgs(options).filter((a)=>!!a).filter((k)=>k.indexOf('undefined')==-1)
const ret = await Helper.run(getSlicrDir(options), SLIC3R_EXE, args, options.debug)
if(ret && ret.code !==0){
logger.error(`Error running Slic3r : `)
return ret
}
if(options.log){
write(options.log,ret.messages.join('\n'))
}
onNode({
...ret,
src: file,
target
})
if(options.saveArgs){
write(options.saveArgs,`${SLIC3R_EXE} ${args.join(' ')}`)
}
if(options.saveAsProfile){
const out = clone(removeEmpty(options))
delete out['src']
delete out['srcInfo']
delete out['dst']
delete out['dstInfo']
delete out['title']
delete out['type']
delete out['properties']
delete out['oneOf']
delete out['$0']
delete out['_']
delete out['skip']
delete out['variables']
delete out['saveAsProfile']
delete out['saveArgs']
delete out['profile']
delete out['debug']
delete out['verbose']
delete out['log']
write(options.saveAsProfile, out )
}
if (osr_cache) {
options.debug && logger.info('Write output to cache', _target)
await set_cached(file, ca_options, MODULE_NAME, fileAsBuffer(_target))
}
return ret
}
export async function convertFiles(file, targets: string[], onNode: (data: any) => void = () => { }, options: SlicerOptions) {
if (options.dry) {
return Promise.resolve()
}
return BPromise.resolve(targets).map((target) => {
return convertFile(file, target, onNode, options)
}, { concurrency: 1 });
}
export const report = (data, dst: string) => {
let report: any = null;
if (dst.endsWith('.md')) {
//report = reportMarkdown(data);
}
if (dst.endsWith('.csv')) {
report = reportCSV(data);
}
if (report) {
logger.info(`Write report to ${dst}`);
write(dst, report);
}
return report;
}
export const targets = (f: string, options: SlicerOptions) => {
const srcParts = path.parse(f)
const variables = clone(options.variables)
const targets = []
if (options.dstInfo.IS_GLOB) {
options.dstInfo.GLOB_EXTENSIONS.forEach((e) => {
variables.SRC_NAME = srcParts.name
variables.SRC_DIR = srcParts.dir
let targetPath = substitute(options.alt, options.variables.DST_PATH, variables)
targetPath = path.resolve(targetPath.replace(options.variables.DST_FILE_EXT, '') + e)
const parts = path.parse(targetPath)
if (!exists(parts.dir)) {
try {
dir(parts.dir)
} catch (e) {
if (options.debug) {
logger.error(`Error creating target path ${parts.dir} for ${targetPath}`)
}
return
}
}
targets.push(targetPath)
});
}
return targets
}
export async function convert(options: SlicerOptions) {
let reports = []
const onNode = options.onNode || ((data) => reports.push(data))
options.verbose && logger.info(`Convert ${options.srcInfo.FILES.length} files `)
await BPromise.resolve(options.srcInfo.FILES).map((f) => {
const outputs = targets(f, options)
options.verbose && logger.info(`Convert ${f} to `, outputs)
return convertFiles(f, outputs, onNode, options)
}, { concurrency: 1 })
if (options.report) {
const reportOutFile: string = substitute(false, options.report, {
dst: options.srcInfo.DIR
})
options.verbose && logger.info(`Write report to ${reportOutFile}`);
report(reports, reportOutFile)
}
}

View File

@ -0,0 +1,95 @@
import { Browser, launch, Page, Response } from 'puppeteer';
import { inspect, error, debug } from '../../log';
import { capture_requests, capture_responses } from './network';
let instance: Scope;
import * as path from 'path';
import { URL } from 'url';
export const STATS_SUFFIX = '_stats.json';
export const SESSION_EVENTS_SUFFIX = '_session.json';
export const TRACE_SUFFIX = '_trace.json';
const included_categories = ['devtools.timeline'];
const _url_short = (url: string) =>
new URL(url).hostname;
const _date_suffix = () =>
new Date().toLocaleTimeString().replace(/:/g, '_');
const _random_suffix = () =>
Math.random() * 100;
const _default_filename = (url: string) =>
`${_url_short(url)}_${_random_suffix()}`;
export const default_path = (cwd: string, url: string) =>
`${path.join(cwd, _default_filename(url))}${STATS_SUFFIX}`;
export const default_session_events_path = (cwd: string, url: string) =>
`${path.join(cwd || process.cwd(), 'sessions', _default_filename(url))}${SESSION_EVENTS_SUFFIX}`;
export const default_trace_path = (cwd: string, url: string) =>
`${path.join(cwd, _default_filename(url))}${TRACE_SUFFIX}`;
export class Scope {
browser!: Browser;
context!: any;
page!: Page;
args!: any;
requests: any[] = [];
responses: Response[] = [];
eventBeacons: any[] = [];
mutationBeacons: any[] = [];
sessionSuffix: string = '';
onResponse;
onRequest;
async init() {
this.sessionSuffix = ' - ' + new Date().getTime();
const args = [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-infobars',
'--window-position=0,0',
'--ignore-certifcate-errors',
'--ignore-certifcate-errors-spki-list',
'--user-agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3312.0 Safari/537.36"'
];
this.browser = await launch({ headless: this.args.headless === 'true', devtools: true,args:args});
// const context = await this.browser.createIncognitoBrowserContext();
this.page = await this.browser.newPage();
// this.page = await context.newPage();
this.page.on('console', msg => {
// error('Browser error:', msg);
});
this.page.on('error', msg => error('Browser Error:', msg));
// this.page.on('pageerror', msg => error('Browser Page Error:', msg));
// this.page.on('requestfailed', msg => error('Browser Page Request Error:', msg));
//capture_requests(this, this.page);
//capture_responses(this, this.page);
// this.args.disableRequests !== 'true' && capture_requests(this, this.page);
// this.args.disableResponses !== 'true' && capture_requests(this, this.page);
// capture_responses(this, this.page);
const page2 = this.page as any;
//page2.setCacheEnabled(false);
/*
await page2._client.send('Security.setOverrideCertificateErrors', {
override: true
});
*/
await page2._client.on('Security.certificateError', (event: any) => {
page2._client.send('Security.handleCertificateError', {
eventId: event.eventId,
action: 'continue' // ignore error and continue request
})
})
}
}
export const getScope = (cliArgs?: any) => {
if (!instance) {
instance = new Scope();
instance.args = cliArgs;
}
return instance;
}

View File

@ -0,0 +1,25 @@
import { NavigationOptions } from 'puppeteer';
export const defaultTenant = 1;
export const userSessionsTab = (base: string) => `${base}/e/${defaultTenant}/#usersearchoverview;gtf=l_2_HOURS`;
export const loginUrl = (base: string) => `${base}/login`;
export const loginUrlDemoDev = () => `https://proxy-dev.dynatracelabs.com/sso/ProxyLocator.jsp`;
export const defaultPageOptions = (): NavigationOptions => {
return {
timeout: 5000,
waitUntil: 'networkidle2'
}
}
export const replay_api_overview = 'uemshareddetails/rumoverviewdata/usersearchoverview';
const ts = () => {
const d = new Date();
return d.getHours() + '_' + d.getMinutes() + '_' + d.getSeconds();
}
export const sessionName = (url?: string) => `Pupeteer :${ts()}`;
export const maxSessionWaitingTime = 1000 * 60 * 3;
export const responseRetryTime = 1000 * 8;
export const defaultMutationRoot = '#mutationsRoot';
export const defaultMutationTag = 'div';
export const defaultHeavyMutations = 2000;
export const defaultMediumMutations = 500;

View File

@ -0,0 +1,186 @@
import { launch, Page } from 'puppeteer'
import { readFileSync } from 'fs';
import * as moment from 'moment';
import { sync as unlink } from '@plastichub/fs/remove';
import { async as iterator } from '@plastichub/fs/iterator';
import {
debug, inspect,
Options, TraceEntry, TraceTiming,
default_trace_path,
ReportEntry,
NetworkReportEntry,
sizeToString,
log,
spinner,
STATS_SUFFIX,
TRACE_SUFFIX
} from '../../';
import { end_time } from './times';
import { find_time } from './trace';
import { rl } from './stdin';
import { report, find_report, get_report } from './report';
import { IProcessingNode } from '@plastichub/fs/interfaces';
const included_categories = ['devtools.timeline'];
export class Puppeteer {
static clean(url: string, options: Options) {
iterator(options.cwd, {
matching: [`*${STATS_SUFFIX}`, `*${TRACE_SUFFIX}`]
}).then((it) => {
let node: IProcessingNode = null;
while (node = it.next()) {
unlink(node.path);
}
})
}
static async begin(url: string, options: Options) {
const browser = await launch({
headless: options.headless,
devtools: false
});
return await browser.newPage();
}
static async crawler(url: string, options?: Options) {
const page = await this.begin(url, options);
}
static async repl(url: string, options?: Options) {
const page = await this.begin(url, options);
page.on('console', msg => inspect('Console Message:', msg.text()));
await page.goto(url, {
timeout: 600000,
waitUntil: 'networkidle0'
});
const readline = rl(`${url}#`, (line: string) => {
page.evaluate(line).then((results) => {
inspect(`Did evaluate ${line} to `, results);
})
}, () => this.end(page));
}
static async end(page: Page) {
const browser = await page.browser();
await page.close();
await browser.close();
}
static async summary(url: string, options?: Options) {
const browser = await launch({
headless: options.headless,
devtools: true
});
const page = await browser.newPage();
await page.goto(url, {
timeout: 600000,
waitUntil: 'networkidle0'
});
const metrics = await page.metrics();
await this.end(page);
return metrics;
}
static async detail(url: string, options?: Options) {
const network_stats = report();
const ReceivedTotal = get_report(network_stats, 'Received Total');
const ReceivedStyleSheets = get_report(network_stats, 'Received Stylesheets');
const ReceivedScripts = get_report(network_stats, 'Received Scripts');
const ReceivedHTML = get_report(network_stats, 'Received HTML');
const ReceivedImages = get_report(network_stats, 'Received Images');
const ReceivedJSON = get_report(network_stats, 'Received JSON');
const ReceivedFonts = get_report(network_stats, 'Received Fonts');
const ReceivedBinary = get_report(network_stats, 'Received Binary');
const MimeMap = {
'application/javascript': ReceivedScripts,
'text/javascript': ReceivedScripts,
'text/css': ReceivedStyleSheets,
'text/html': ReceivedHTML,
'image/png': ReceivedImages,
'image/gif': ReceivedImages,
'image/svg+xml': ReceivedImages,
'application/json': ReceivedJSON,
'application/octet-stream': ReceivedBinary,
'font/woff2': ReceivedFonts,
'application/font-woff2': ReceivedFonts
}
const traceFile = default_trace_path(options.cwd, url);
const page = await this.begin(url, options);
await page.tracing.start({
path: traceFile,
categories: included_categories
});
await page.goto(url, {
timeout: 600000,
waitUntil: 'networkidle0'
});
const metrics = await (page as any)._client.send('Performance.getMetrics');
const nowTs = new Date().getTime();
// const navigationStart = getTimeFromMetrics(metrics, 'NavigationStart');
const navigationStart = find_time(metrics, 'Timestamp') + nowTs;
await page.tracing.stop();
// --- extracting data from trace.json ---
const tracing = JSON.parse(readFileSync(traceFile, 'utf8'));
const dataReceivedEvents = tracing.traceEvents.filter(x => x.name === 'ResourceReceivedData');
const dataResponseEvents = tracing.traceEvents.filter(x => x.name === 'ResourceReceiveResponse');
// find resource in responses or return default empty
const content_response = (requestId: string): TraceEntry => dataResponseEvents.find((x) =>
x.args.data.requestId === requestId)
|| { args: { data: { encodedDataLength: 0 } } };
const report_per_mime = (mime: string): NetworkReportEntry => MimeMap[mime] || get_report(network_stats, mime);
// our iteration over the trace
// @TODO: convert to a better tree structure to avoid O(n) lookups
// @TODO: emit to extensions: events & aspects
// @TODO: calculate times
// @TODO: filter
// @TODO: options.mask
// @TODO: this iterator might get async
ReceivedTotal.value = dataReceivedEvents.reduce((first, x) => {
const content = content_response(x.args.data.requestId);
const data = content.args.data;
const report = report_per_mime(data.mimeType);
if (data.fromCache === false) {
report.value += x.args.data.encodedDataLength
report.count++;
} else {
report.cached_count++;
}
ReceivedTotal.count++;
return first + x.args.data.encodedDataLength;
}, ReceivedTotal.value);
// calulate finals
[ReceivedTotal, ReceivedHTML, ReceivedImages, ReceivedJSON,
ReceivedScripts, ReceivedFonts, ReceivedBinary
].forEach((r) => r.formatted = sizeToString(r.value))
// --- end extracting data from trace.json ---
let results = [];
// lights off
await this.end(page);
return {
times: [],
network: network_stats
}
}
}

View File

@ -0,0 +1,176 @@
import { Page, Request, Response } from 'puppeteer';
import { sessionName, maxSessionWaitingTime, defaultPageOptions, userSessionsTab } from './constants';
import * as debug from '../../log';
import { Scope } from './Scope';
import { parse } from 'url';
import { navigateToUserSessions } from './processes';
const debugRequests = true;
const debugResponses = false;
export const default_postdata = (request: Request): any => request.postData && request.postData() || {};
export type ResponseMatch = (request: any) => boolean;
export const HasUserSessions = (request: Request) => (default_postdata(request).users)
export const MyUserSessions = (url: string, request: Request) => SessionWithName(request, sessionName(url));
export const SessionWithName = (request: Request, name: string) => {
const data = default_postdata(request).users || [];
return data.find((user: any) => user.id === name)
}
export type ResponseResolve = Response & {
data: any;
}
const default_prepare = (requests: Request[]): Request[] => {
return requests;
};
const default_filter_json = (r: Request) => ((r.headers()['content-type'] || '').startsWith('application/json;')) === true;
const responses = async function (requests: Request[]) { return Promise.all(requests.map(r => r.response()!.json())) };
export const findRequest = (url: string, requests: Request[], match?: ResponseMatch): Request[] => {
url = decodeURIComponent(url);
if (!match) {
return requests.filter((request) => request.url().indexOf(url) !== -1);
} else {
const results = requests.filter((request) => request.url().indexOf(url) !== -1);
return results.filter((r) => match!(r));
}
}
export function waitForResponse(url: string, scope: Scope, match: ResponseMatch, timeout: number = 5000): Promise<any[]> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
let requests = default_prepare(scope.requests).filter(default_filter_json).filter(r => r.response() != null);
requests = requests.filter((request) => request.url().indexOf(url) !== -1);
responses(requests).then((responses) => {
const ret = responses.filter(match);
if (ret.length) {
resolve(ret);
} else {
reject('cant find anything yet');
}
});
} catch (error) {
debug.error('waitForResponse Error ', error);
}
}, timeout);
})
}
export function waitForResponseNTimes(url: string, scope: Scope, match: ResponseMatch, timeout: number = 5000) {
return new Promise((resolve, reject) => {
const maxTime = maxSessionWaitingTime;
const retryTime = 8000;
let reachedTimeOut = false;
let interval: any = null;
interval = setInterval(() => {
if (reachedTimeOut) {
clearInterval(interval);
debug.error('reached max');
reject('reached maximum timeout');
return;
}
onReload(scope).then(() => {
scope.page.reload().then(() => {
debug.info('retry ');
waitForResponse(url, scope, match, retryTime).then((session) => {
debug.inspect('got my session', session);
clearInterval(interval);
resolve(session);
}).catch((e) => {
debug.error('found nothing');
})
}).catch((e) => {
console.error('error loading page : ', e);
});
});
}, retryTime);
setTimeout(() => {
reachedTimeOut = true;
clearInterval(interval);
reject('max timeout reached');
}, maxTime);
});
};
export async function capture_request(where: any[], request: Request) {
debugRequests && debug.inspect('Request', { url: request.url(), data: request.postData() });
where.push({ url: request.url(), data: await request.postData(), request: request });
debugRequests && debug.inspect('requests', where.map(r => r.url));
}
export async function capture_response(where: any[], response: Response) {
debugResponses && debug.inspect('Response', { url: response.url(), data: await response.json() });
where.push(response);
}
export async function capture_requests(scope: Scope, page: Page) {
await page.setRequestInterception(true);
scope.requests = [];
page.on('request', (interceptedRequest: Request) => {
if(scope.onRequest){
scope.onRequest(interceptedRequest, scope);
return;
}
try {
const url = decodeURIComponent(interceptedRequest.url());
const parsed = parse(url, true);
if (url.includes('.css') || url.includes('.svg')) {
interceptedRequest.abort();
return;
}
const query = parsed.query;
const isJson = (interceptedRequest.headers()['content-type'] || '').startsWith('application/json') === true;
if (isJson) {
// capture_request(scope.requests, interceptedRequest);
//debugRequests && debug.inspect('q ' + query['contentType'] + ' ' + url);
}
interceptedRequest.continue();
} catch (e) {
debug.error('error parsing request ', e);
}
});
}
export async function capture_responses(scope: Scope, page: Page) {
try {
// await page.setRequestInterception(true);
} catch (e) {
debug.error('error intercepting responses', e);
}
scope.responses = [];
page.on('response', response => {
try {
const isJson = (response.headers()['content-type'] || '').startsWith('application/json;') === true;
const url = response.url();
if (response.status() === 200) {
if (isJson) {
capture_response(scope.responses, response);
}
if (scope.onResponse) {
scope.onResponse(response, scope);
}
} else {
debugResponses && debug.error(`Error loading ${url} : ${response.status()}`);
}
} catch (e) {
debugResponses && debug.error('Error parsing response');
}
});
}
export async function onReload(scope: Scope) {
scope.requests = [];
try {
await scope.page.setRequestInterception(false);
} catch (e) {
}
await scope.page.setRequestInterception(true);
}

View File

@ -0,0 +1,78 @@
import { Page, Request } from 'puppeteer';
import { Options } from './types';
import { loginUrl, defaultPageOptions, userSessionsTab, loginUrlDemoDev } from './constants';
export async function loginFrontEnd(page: Page, options: Options) {
// await page.goto(loginUrl(options.dynatraceUrl), defaultPageOptions());
await page.type('#user', 'admin');
await page.type('#password', 'admin');
await page.click('#login-form > .maindiv > .logindiv > .submitdiv > .button');
await page.waitForSelector('.tenant-selector');
await page.click('.textboxdiv > .tenant-selector:nth-child(2)');
}
export async function loginDemoDev(page: Page, options: Options) {
await page.goto(loginUrlDemoDev(), defaultPageOptions());
await page.type('#IDToken1', 'guenter.baumgart@ruxit.com');
await page.click('.maindiv > .logindiv > form > div > #formsubmit');
return new Promise((resolve) => {
setTimeout(() => {
resolve(1);
console.log('time out');
}, 20 * 1000)
});
console.log('logged in 1/2!');
/*
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.waitForSelector('.logindiv > form > .textboxdiv > .emailLoginIcon > #IDToken1')
await page.click('.logindiv > form > .textboxdiv > .emailLoginIcon > #IDToken1')
await page.waitForSelector('.maindiv > .logindiv > form > div > #formsubmit')
await page.click('.maindiv > .logindiv > form > div > #formsubmit')
const navigationPromise = page.waitForNavigation()
await navigationPromise
await page.waitForSelector('.logindiv > form > .margin-bottom\3A > .passwordIcon > #IDToken2')
await page.click('.logindiv > form > .margin-bottom\3A > .passwordIcon > #IDToken2')
await page.waitForSelector('.logindiv > form > fieldset > div > #IDToken3')
await page.click('.logindiv > form > fieldset > div > #IDToken3')
await page.waitForSelector('form > fieldset > #button-base > div > #loginButton_0')
await page.click('form > fieldset > #button-base > div > #loginButton_0')
await navigationPromise
await navigationPromise
await navigationPromise
await browser.close()
})()
*/
//await page.type('#password', 'admin');
//await page.click('#login-form > .maindiv > .logindiv > .submitdiv > .button');
//await page.waitForSelector('.tenant-selector');
//await page.click('.textboxdiv > .tenant-selector:nth-child(2)');
}
export async function navigateToUserSessions(page: Page, options: Options) {
// await page.goto(userSessionsTab(options.dynatraceUrl), defaultPageOptions());
}
export async function navigateToUserLocalhost(page: Page, options?: Options) {
// await page.goto('http://localhost/', defaultPageOptions());
}

View File

@ -0,0 +1,40 @@
import { NetworkReportEntry } from './types';
const report_item = (name: string): NetworkReportEntry => {
return {
name: name,
value: 0,
formatted: '',
count: 0,
cached_count: 0,
external_count: 0,
local_count: 0,
times: {
end: 0,
formatted: ''
}
}
}
export const report = () => {
return [
report_item('Received Total'),
report_item('Received Stylesheets'),
report_item('Received Scripts'),
report_item('Received HTML'),
report_item('Received JSON'),
report_item('Received Images'),
report_item('Received Fonts'),
report_item('Received Binary')
]
}
export const find_report = (where: any[], name: string) => where.find((media) => media.name === name);
export const get_report = (where: any[], type: string) => {
let record = find_report(where, type);
if (!record) {
record = report_item(type);
where.push(record);
}
return record;
}

View File

@ -0,0 +1,31 @@
import { createInterface, ReadLine } from 'readline';
import { Page } from 'puppeteer';
import chalk from 'chalk';
export const rl = (prompt: string, onLine?: (line: string) => void, onClose?: () => {}) => {
const rl = createInterface({
input: process.stdin,
output: process.stdout,
prompt: chalk.green(prompt)
})
console.log('start stdin: ' + prompt);
rl.prompt()
rl.on('line', (line) => {
if(!line){
return;
}
try {
onLine(line);
} catch (e) {
console.error(e)
rl.prompt()
}
})
rl.on('close', () => {
rl.close()
onClose();
});
return rl;
}

View File

@ -0,0 +1,12 @@
import { TraceEntry } from './types'
export const end_time = (entry: TraceEntry): number => {
const timing = entry.args.data.timing;
const start = timing.requestTime;
const received = entry.ts;
//it might be more accurate to calc this with the header times
const headersReceived = start + timing.receiveHeadersEnd / 1000;
let responseReceived = headersReceived;
responseReceived = Math.min(responseReceived, headersReceived);
responseReceived = Math.max(responseReceived, start);
return Math.max(-1, responseReceived);
}

View File

@ -0,0 +1,57 @@
import { NetworkReportEntry, TraceEntry } from "./types";
export const find_time = (metrics, name) =>
metrics.metrics.find(x => x.name === name).value;
export const all_with_name = (where: NetworkReportEntry[], name: string) =>
where.filter((entry) => entry.name === name);
/*
export const tree = (entries: TraceEntry[]) => {
const tasks = [];
let currentTask;
for (const event of entries) {
// Only look at X (Complete), B (Begin), and E (End) events as they have most data
if (event.ph !== 'X' && event.ph !== 'B' && event.ph !== 'E') continue;
// Update currentTask based on the elapsed time.
// The next event may be after currentTask has ended.
while (
currentTask &&
Number.isFinite(currentTask.endTime) &&
currentTask.endTime <= event.ts
) {
currentTask = currentTask.parent;
}
if (!currentTask) {
// We can't start a task with an end event
if (event.ph === 'E') {
throw new Error('Fatal trace logic error');
}
currentTask = entries._createNewTaskNode(event);
tasks.push(currentTask);
continue;
}
if (event.ph === 'X' || event.ph === 'B') {
// We're starting a nested event, create it as a child and make it the currentTask
const newTask = entries._createNewTaskNode(event, currentTask);
tasks.push(newTask);
currentTask = newTask;
} else {
if (currentTask.event.ph !== 'B') {
throw new Error('Fatal trace logic error');
}
// We're ending an event, update the end time and the currentTask to its parent
currentTask.endTime = event.ts;
currentTask = currentTask.parent;
}
}
return tasks;
}
*/

View File

@ -0,0 +1,124 @@
/////////////////////////////////////////////////////
//
// Application types
//
export enum OutputTarget {
STDOUT = 'console',
FILE = 'file'
}
export enum OutputFormat {
text = 'text',
json = 'json'
}
export interface Options {
// @TODO: support many
url?: string;
format?: OutputFormat;
// @TODO: support many
target?: OutputTarget;
headless?: boolean;
// output path
path?: string;
// @TODO: required to pick profile/config files
cwd?: string;
// @TODO: time of sesssion, mapped to Puppeteer waitUntil, if it's a number, the session will be opened for that
// time window, time=-1 means infinity, useful for repl. sessions
time?: number;
// @TODO: reload interval
reload?: number;
// @TODO: repl. --repl=true=interactive or repl=path to specify script
repl?: string | boolean;
// TODO: colored ouput
colors?: boolean;
}
// options for certain categories as network, rendering,...
export interface OptionEx {
include?: string | string[];
exclude?: string | string[];
query?: string | string[];
}
export type OptionsEx = Options & {
launchOptions?: {
// puppeteer launch options
}
waitOptions?: {
// puppeteer wait options: wait for selector,...
}
replOptions?: {
script?: string;
}
}
export type OutputResult = boolean;
export interface ReportEntry {
name: string;
}
export type NetworkReportEntry = ReportEntry & {
value: number;
formatted: string;
count: number;
cached_count: number;
external_count: number;
local_count: number;
times?: {
end: number,
formatted: string;
}
}
/////////////////////////////////////////////////////
//
// Foreign data types (trace data)
//
// type for a network resource's timing
export interface TraceTiming {
requestTime: number;
proxyStart: number;
proxyEnd: number;
dnsStart: number;
dnsEnd: number;
connectStart: number;
connectEnd: number;
sslStart: number;
sslEnd: number;
workerStart: number;
workerReady: number;
sendStart: number;
sendEnd: number;
receiveHeadersEnd: number;
pushStart: number;
pushEnd: number;
}
export interface TraceData {
requestId: string;
frame: string;
statusCode: number;
mimeType: string;
encodedDataLength: number;
fromCache: boolean;
fromServiceWorker: boolean;
timing: TraceTiming;
}
export interface TraceArgs {
data: TraceData;
}
export interface TraceEntry {
pid: number;
tid: number;
ts: number;
ph: string; // B: begin, | E: end; For async events: S: start, F: finish
cat: string;
name: string;
args: TraceArgs;
tts: number;
s: string;
}

View File

@ -0,0 +1,54 @@
import * as path from 'path';
const csv = require('csv-stringify/lib/sync');
const OSR_REGEX = /^[0-9].+$/;
const isOSR = (filename) => filename.match(OSR_REGEX) != null;
export const reportCSV = (data) => {
const image = (path) => `![](./${(encodeURI(path))})`;
const file = (path) => `[${path}](./${(encodeURI(path))})`;
const qty = (filename) => {
if (isOSR(filename)) {
const parts = filename.split("_");
const partsLength = parts.length;
const token = parts[partsLength - 3];
if (token) {
return parseInt(token.replace('x', ''));
}
}
return 'unknown';
}
const thickness = (filename) => {
if (isOSR(filename)) {
const parts = filename.split("_");
const partsLength = parts.length;
const token = parts[partsLength - 2];
if (token) {
return token;
}
}
return 'unknown';
}
const set = data.map((d) => [
`${path.parse(d.target).name}`,
`${image(path.parse(d.target).name + path.parse(d.target).ext)}`,
`${file(path.parse(d.src).name + path.parse(d.src).ext)}`,
`${qty(path.parse(d.src).name)}`,
`${thickness(path.parse(d.src).name)}`,
`${isOSR(path.parse(d.src).name) ? 'Laser' : 'Unkown'}`,
''
]
);
const csvString = csv(set, {
header: true,
delimiter: ',',
columns: { 'a': 'Name', 'b': 'Thumbnail', 'c': 'File', 'd': 'Qty', 'f': 'Thickness', 'g': 'Type', 'h': 'Missing' }
});
return csvString;
}

View File

@ -0,0 +1 @@
export { reportCSV } from './csv'

View File

@ -0,0 +1,218 @@
import * as CLI from 'yargs'
import * as path from 'path'
import {
SolidworkOptions,
SlicerOptions,
logger
} from './'
import { pathInfo, resolve } from "@plastichub/osr-commons"
import { sync as read } from "@plastichub/fs/read"
import { sync as exists } from "@plastichub/fs/exists"
import { substitute } from './'
export const defaultOptions = (yargs: CLI.Argv) => {
return yargs.option('src', {
default: './',
describe: 'The source directory or source file. Glob patters are supported!',
demandOption: true
}).option('dst', {
describe: 'Destination folder or file'
}).option('Report', {
describe: 'Optional conversion report. Can be JSON, HTML, CSV or Markdown'
}).option('debug', {
default: false,
describe: 'Enable internal debug messages',
type: 'boolean'
}).option('alt', {
default: false,
describe: 'Use alternate tokenizer, & instead of $',
type: 'boolean'
}).option('skip', {
default: true,
describe: 'Skip existing files',
type: 'boolean'
}).option('dry', {
default: false,
describe: 'Run without conversion',
type: 'boolean'
}).option('verbose', {
default: true,
describe: 'Show internal messages',
type: 'boolean'
}).option('saveArgs', {
describe: 'Save command line options to a file',
type: 'string'
}).option('saveAsProfile', {
describe: 'Save command line options to a json file. To be loaded via --profile=file.json',
type: 'string'
}).option('profile', {
describe: 'Load options from a json file',
type: 'string'
}).option('log', {
describe: 'Save Slic3r output to a file',
type: 'string'
})
}
// Sanitizes faulty user argv options for all commands.
export const sanitizeSingle = (argv: CLI.Arguments): SolidworkOptions => {
const src = path.resolve('' + argv.src);
const config: any = argv.config ? read(path.resolve('' + argv.config), 'json') : {};
const extraVariables = {};
for (const key in config) {
if (Object.prototype.hasOwnProperty.call(config, key)) {
const element = config[key];
if (typeof element === 'string') {
extraVariables[key] = element;
}
}
}
const args: any = {
src: src,
dst: '' + argv.dst as string,
report: argv.report ? path.resolve(argv.report as string) : null,
debug: argv.debug,
verbose: argv.verbose,
dry: argv.dry,
cache: argv.skip,
alt: argv.alt,
// glob: argv.glob as string,
variables: { ...extraVariables },
args: argv.args || ''
} as any
if (!args.src) {
logger.error('Invalid source, abort');
return process.exit()
}
args.srcInfo = pathInfo(argv.src as string);
if (!args.srcInfo.FILES) {
logger.error(`Invalid source files, abort`);
return process.exit()
}
for (const key in args.srcInfo) {
if (Object.prototype.hasOwnProperty.call(args.srcInfo, key)) {
args.variables['SRC_' + key] = args.srcInfo[key]
}
}
if (argv.dst) {
args.dst = path.resolve(args.dst)
args.dstInfo = pathInfo(args.dst as string)
args.dstInfo.PATH = path.resolve(argv.dst as string)
for (const key in args.dstInfo) {
if (Object.prototype.hasOwnProperty.call(args.dstInfo, key)) {
args.variables['DST_' + key] = args.dstInfo[key]
}
}
}
return args as any
}
export const sanitize = (argv: any): SlicerOptions => {
const src = path.resolve('' + argv.src)
const config: any = argv.config ? read(path.resolve('' + argv.config), 'json') : {}
const extraVariables = {};
for (const key in config) {
if (Object.prototype.hasOwnProperty.call(config, key)) {
const element = config[key];
if (typeof element === 'string') {
extraVariables[key] = element;
}
}
}
let args = {
src: src,
dst: '' + argv.dst as string,
report: argv.report,
debug: argv.debug,
verbose: argv.verbose,
dry: argv.dry,
onNode: argv.onNode,
cache: argv.skip,
alt: argv.alt,
variables: {
...extraVariables
},
...argv
} as SlicerOptions
args.extruderOffset = args.extruderOffset ? args.extruderOffset : { x: 0, y: 0 }
if (!args.src) {
logger.error('Invalid source, abort')
return
}
args.srcInfo = pathInfo(argv.src as string)
if (!args.srcInfo.FILES) {
logger.error(`Invalid source files, abort`, args.srcInfo)
return process.exit()
}
for (const key in args.srcInfo) {
if (Object.prototype.hasOwnProperty.call(args.srcInfo, key)) {
args.variables['SRC_' + key] = args.srcInfo[key]
}
}
if (argv.saveAsProfile) {
args.saveAsProfile = path.resolve(resolve(args.saveAsProfile, args.alt, args.variables))
}
if (argv.saveArgs) {
args.saveArgs = path.resolve(resolve(args.saveArgs, args.alt, args.variables))
}
if (argv.report) {
args.report = path.resolve(resolve(args.report, args.alt, args.variables))
}
if (argv.profile) {
args.profile = path.resolve(resolve(args.profile, args.alt, args.variables))
}
if (argv.dst) {
args.dst = path.resolve(resolve(args.dst, args.alt, args.variables))
args.dstInfo = pathInfo(args.dst as string)
args.dstInfo.PATH = argv.dst as string
for (const key in args.dstInfo) {
if (Object.prototype.hasOwnProperty.call(args.dstInfo, key)) {
args.variables['DST_' + key] = args.dstInfo[key]
}
}
}
if (argv.profile) {
args.profile = path.resolve(resolve(args.profile, args.alt, args.variables))
if (exists(args.profile)) {
const profile = read(args.profile, 'json') as SlicerOptions
if (profile) {
args = {
...args,
...profile
}
}
}
}
if (argv.log) {
args.log = path.resolve(substitute(args.alt, args.log, args.variables))
}
return args
}

265
packages/cad/src/sw_argv.ts Normal file
View File

@ -0,0 +1,265 @@
import * as CLI from 'yargs'
import * as path from 'path'
import { substitute } from "@plastichub/core/strings"
import { resolve, forward_slash, pathInfo } from "@plastichub/osr-commons"
import { sync as read } from "@plastichub/fs/read"
import { SolidworkOptions, logger } from './'
import { DEFAULT_REPORT } from './constants'
export const defaultOptions = (yargs: CLI.Argv) => {
return yargs.option('src', {
default: './',
describe: 'The source directory or source file. Glob patters are supported!',
demandOption: true
}).option('format', {
describe: 'The target format. Multiple formats are allowed as well, use --format=pdf --format=jpg'
}).option('dst', {
describe: 'Destination folder or file'
}).option('view', {
default: 'Render',
describe: 'Sets the target view'
}).option('Report', {
describe: 'Optional conversion report. Can be JSON, HTML, CSV or Markdown'
}).option('debug', {
default: false,
describe: 'Enable internal debug messages',
type: 'boolean'
}).option('alt', {
default: false,
describe: 'Use alternate tokenizer, & instead of $',
type: 'boolean'
}).option('report', {
default: DEFAULT_REPORT,
describe: '',
type: 'string'
}).option('configuration', {
default: 'Default',
describe: 'Set the Model Configuration to be used',
type: 'string',
alias: 'c'
}).option('cache', {
default: false,
describe: 'Enable caching',
type: 'boolean'
}).option('hidden', {
describe: 'Hide Solidworks window',
type: 'string'
}).option('dry', {
default: false,
describe: 'Run without conversion',
type: 'boolean'
}).option('verbose', {
default: true,
describe: 'Show internal messages',
type: 'boolean'
}).option('quality', {
default: 2,
describe: 'Raytrace quality',
type: 'number'
}).option('renderer', {
default: 'Solidworks',
describe: 'Renderer to be used: Solidworks or Photoview',
type: 'string',
}).option('sw', {
describe: 'Set explicit the path to the Solidworks binaries & scripts.\
Otherwise, set it to 2020, 2022 or 2023 to use the built-in binaries',
default: 2024,
type: 'number'
}).option('swv', {
describe: 'Internal Solidworks Version. Use \'30\' for 2022',
default: 32,
type: 'number'
}).option('pack', {
describe: 'Pack and Go an Assembly. The destination must be a folder',
default: false,
type: 'boolean'
}).option('rebuild', {
describe: 'Rebuild the assembly',
default: false,
type: 'boolean'
}).option('save', {
describe: 'Save the assembly or part',
default: false,
type: 'boolean'
}).option('light', {
describe: 'Open assembly in light mode',
default: false,
type: 'boolean'
}).option('script', {
describe: 'Set explicit the path to the Solidworks script',
default: 'convert.exe'
}).option('bom-config', {
describe: 'Set the Model Configuration to be used',
default: 'Default'
}).option('bom-template', {
describe: 'Path to the BOM template. Default is osr-cad/sw/bom-all.sldbomtbt'
}).option('bom-type', {
describe: 'Bom Type : default = 2 - PartsOnly = 1 | TopLevelOnly = 2 | Indented = 3',
type: "number",
default: 2
}).option('bom-detail', {
describe: 'Bom Numbering : default = 1 - Type_None = 0 | Type_Detailed = 1 | Type_Flat = 2 | BOMNotSet = 3',
type: "number",
default: 1
}).option('bom-images', {
describe: 'Add an image in the first colum',
type: 'boolean',
default: false
})
}
export const sanitizeSingle = (argv: CLI.Arguments): SolidworkOptions => {
const src = forward_slash(path.resolve(resolve(argv.src as string)))
const config: any = argv.config ? read(path.resolve('' + argv.config), 'json') : {}
const extraVariables = {}
for (const key in config) {
if (Object.prototype.hasOwnProperty.call(config, key)) {
const element = config[key]
if (typeof element === 'string') {
extraVariables[key] = element
}
}
}
const args: SolidworkOptions = {
src: src,
dst: '' + argv.dst as string,
debug: argv.debug,
verbose: argv.verbose,
dry: argv.dry,
cache: argv.cache,
alt: argv.alt,
quality: argv.quality,
clear: argv.clear,
renderer: argv.renderer || "solidworks",
close: argv.close,
width: argv.width || "1024",
height: argv.height || "1024",
hidden: argv.hidden || "true",
configuration: argv.configuration || 'Default',
script: argv.script || 'convert.exe',
sw: argv.sw || 2024,
swv: argv.swv || 32,
view: argv.view || '*Render',
pack: argv.pack,
light: argv.light,
rebuild: argv.rebuild,
save: argv.save,
write: argv.write,
variables: { ...extraVariables },
report: argv.report || DEFAULT_REPORT,
args: argv.args || ''
} as SolidworkOptions
if (!args.src) {
logger.error('Invalid source, abort')
return process.exit()
}
args.srcInfo = pathInfo(argv.src as string)
if (!args.srcInfo.FILES) {
logger.error(`Invalid source files, abort`);
return process.exit()
}
for (const key in args.srcInfo) {
if (Object.prototype.hasOwnProperty.call(args.srcInfo, key)) {
args.variables['SRC_' + key] = args.srcInfo[key]
}
}
if (argv.dst) {
args.dst = path.resolve(args.dst)
args.dstInfo = pathInfo(args.dst as string)
args.dstInfo.PATH = path.resolve(argv.dst as string)
for (const key in args.dstInfo) {
if (Object.prototype.hasOwnProperty.call(args.dstInfo, key)) {
args.variables['DST_' + key] = args.dstInfo[key]
}
}
}
return args
}
export const sanitize = (argv: any): SolidworkOptions => {
const src = forward_slash(path.resolve(resolve(argv.src)))
const config: any = argv.config ? read(path.resolve('' + argv.config), 'json') : {}
const extraVariables = {}
for (const key in config) {
if (Object.prototype.hasOwnProperty.call(config, key)) {
const element = config[key];
if (typeof element === 'string') {
extraVariables[key] = element
}
}
}
const args: SolidworkOptions = {
src: src,
dst: '' + argv.dst as string,
debug: argv.debug,
verbose: argv.verbose,
dry: argv.dry,
onNode: argv.onNode,
cache: argv.cache,
hidden: argv.hidden || "true",
renderer: argv.renderer || "solidworks",
alt: argv.alt,
quality: argv.quality,
logLevel: argv.logLevel,
close: argv.close,
width: argv.width || "1024",
height: argv.height || "1024",
script: argv.script || 'convert.exe',
sw: argv.sw || 2024,
swv: argv.swv || 32,
configuration: argv.configuration || 'Default',
report: argv.report || DEFAULT_REPORT,
pack: argv.pack,
light: argv.light,
rebuild: argv.rebuild,
save: argv.save,
write: argv.write,
variables: { ...extraVariables },
view: argv.view || 'Render',
args: argv.args || '',
"bom-config": argv['bom-config'],
"bom-detail": argv['bom-detail'],
"bom-template": argv['bom-template'],
"bom-type": argv['bom-type'],
"bom-images": argv['bom-images'],
} as SolidworkOptions
if (!args.src) {
logger.error('Invalid source, abort')
return process.exit()
}
args.srcInfo = pathInfo(src)
if (!args.srcInfo.FILES) {
logger.error(`Invalid source files, abort`, args.srcInfo)
return process.exit()
}
for (const key in args.srcInfo) {
if (Object.prototype.hasOwnProperty.call(args.srcInfo, key)) {
args.variables['SRC_' + key] = args.srcInfo[key]
}
}
if (argv.dst) {
args.dst = path.resolve(substitute(args.dst, args.variables))
args.dstInfo = pathInfo(args.dst as string)
args.dstInfo.PATH = argv.dst as string
for (const key in args.dstInfo) {
if (Object.prototype.hasOwnProperty.call(args.dstInfo, key)) {
args.variables['DST_' + key] = args.dstInfo[key]
}
}
}
(args as SolidworkOptions).view = argv.view as string || "Render"
return args
}

48
packages/cad/src/tree.cs Normal file
View File

@ -0,0 +1,48 @@
using Newtonsoft.Json;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
using System.Collections.Generic;
public class TreeItem
{
public string Name { get; set; }
public List<TreeItem> Children { get; set; }
}
public string CreateAssemblyTreeJson(string filePath)
{
SldWorks swApp = new SldWorks();
ModelDoc2 swModel = swApp.OpenDoc6(filePath, (int)swDocumentTypes_e.swDocASSEMBLY, (int)swOpenDocOptions_e.swOpenDocOptions_Silent, "", 0, 0);
AssemblyDoc swAssembly = (AssemblyDoc)swModel;
TreeItem root = new TreeItem();
root.Name = swModel.GetTitle();
List<TreeItem> children = new List<TreeItem>();
TraverseComponents(swAssembly.GetComponents(false), children);
root.Children = children;
swApp.CloseDoc(swModel.GetTitle());
return JsonConvert.SerializeObject(root);
}
private void TraverseComponents(Component2[] components, List<TreeItem> parentList)
{
foreach (Component2 component in components)
{
TreeItem item = new TreeItem();
item.Name = component.Name2;
if (component.GetChildrenCount() > 0)
{
List<TreeItem> children = new List<TreeItem>();
TraverseComponents(component.GetChildren(), children);
item.Children = children;
}
parentList.Add(item);
}
}

228
packages/cad/src/types.ts Normal file
View File

@ -0,0 +1,228 @@
import { PATH_INFO } from "@plastichub/osr-commons"
export interface IOptionsCache {
cache?: boolean
clear?: boolean
}
export interface IOptionsBase extends IOptionsCache {
src: string
srcInfo?: PATH_INFO
dstInfo?: PATH_INFO
dst?: string
alt?: boolean
debug?: boolean
verbose?: boolean
dry?: boolean
report?: string
variables: Record<string, string>
script?: string
args?: string
onNode: (data:INodeCallback) => Promise<void>
}
export interface INodeCallback {
src: string
target: string
options: IOptionsBase
}
//////////////////////////////////////////////////////////////
//
// Solidworks
export interface IBomOptions {
'bom-template'?: string
'bom-detail'?: number
'bom-type'?: number
'bom-images'?: boolean
'bom-config'?: string
}
export interface SolidworkOptions extends IOptionsBase, IBomOptions {
close?: boolean
configuration?: string
height?: number
hidden?: string
light?: boolean
logLevel?: string
pack?: boolean
quality?: number
rebuild?: boolean
renderer?: string
save?: boolean
sw?: string
swv?: number
view?: string
width?: number
write?: boolean
}
//////////////////////////////////////////////////////////////
//
// Slic3r
export interface IPrintCenter {
x: number
y: number
}
export type EGCodeFlavor = 'reprap' | 'marlin' | 'teacup' | 'makerware' | 'sailfish' | 'mach3' | 'noextrusion'
export interface IBedSize {
width: number
height: number
}
export interface IExtruderOffset {
x: number
y: number
}
export type TOutputResult = boolean
export interface ISlic3rCLIOptions {
avoidCrossingPerimeters?: boolean
bedSize?: IBedSize
bedTemperature?: number
bottomSolidLayers?: number
bridgeAcceleration?: number
bridgeFanSpeed?: number
bridgeFlowRatio?: number
bridgeSpeed?: number
brimWidth?: number
completeObjects?: boolean
cooling?: boolean
cut?: number
defaultAcceleration?: number
disableFanFirstLayers?: number
dontSupportBridges?: boolean
duplicate?: number
duplicateDistance?: number
duplicateGrid?: string
endGCode?: boolean
endGcode?: boolean
exportSvg?: boolean
externalPerimeterExtrusionWidth?: number | string
externalPerimetersFirst?: boolean
externalPerimeterSpeed?: number
extraPerimeters?: boolean
extruderClearanceHeight?: number
extruderClearanceRadius?: number
extruderOffset?: IExtruderOffset
extrusionMultiplier?: number
extrusionWidth?: number | string
fanAlwaysOn?: boolean
fanBelowLayerTime?: number
filamentDiameter?: number
fillAngle?: number
fillDensity?: number
fillPattern?: string
firstLayerAcceleration?: number
firstLayerBedTemperature?: number
firstLayerExtrusionWidth?: number | string
firstLayerHeight?: number
firstLayerSpeed?: number
firstLayerTemperature?: number
g0?: boolean
gapFillSpeed?: number
GCodeArcs?: boolean
GCodeComments?: boolean
GCodeFlavor?: EGCodeFlavor
infillAcceleration?: number
infillEveryLayers?: number
infillExtruder?: number
infillExtrusionWidth?: number | string
infillFirst?: boolean
infillOnlyWhereNeeded?: boolean
infillSpeed?: number
info?: boolean
inputFile?: string
layerGCode?: boolean
layerGcode?: boolean
layerHeight?: number
load?: string
log?: string
maxFanSpeed?: number
merge?: boolean
minFanSpeed?: number
minPrintSpeed?: number
minSkirtLength?: number
notes?: string
nozzleDiameter?: number
onlyRetractWhenCrossingPerimeters?: boolean
oozePrevention?: boolean
outputDirectory?: string
outputFile?: string
outputFilenameFormat?: string
overhangs?: boolean
perimeterAcceleration?: number
perimeterExtruder?: number
perimeterExtrusionWidth?: number | string
perimeters?: number
perimeterSpeed?: number
postProcess?: string[]
postProcessScripts?: string[]
pressureAdvance?: number
printCenter?: IPrintCenter
profile?: string
raftLayers?: number
repair?: boolean
resolution?: number
retractBeforeTravel?: number
retractLayerChange?: boolean
retractLength?: number
retractLengthToolchange?: number
retractLift?: number
retractRestartExtra?: number
retractRestartExtraToolchange?: number
retractSpeed?: number
rotate?: number
save?: string
saveArgs?: string
saveAsProfile?: string
scale?: number
seamPosition?: 'random' | 'nearest' | 'aligned'
skirtDistance?: number
skirtHeight?: number
skirts?: number
slowdownBelowLayerTime?: number
smallPerimeterSpeed?: number
solidFillPattern?: string
solidInfillBelowArea?: number
solidInfillEveryLayers?: number
solidInfillExtruder?: number
solidInfillExtrusionWidth?: number | string
solidInfillSpeed?: number
solidLayers?: boolean
spiralVase?: boolean
split?: boolean
standbyTemperatureDelta?: number
startGCode?: boolean
startGcode?: boolean
supportMaterial?: boolean
supportMaterialAngle?: number
supportMaterialEnforceLayers?: number
supportMaterialExtruder?: number
supportMaterialExtrusionWidth?: number | string
supportMaterialInterfaceExtruder?: number
supportMaterialInterfaceLayers?: number
supportMaterialInterfaceSpacing?: number
supportMaterialInterfaceSpeed?: number
supportMaterialPattern?: string
supportMaterialSpacing?: number
supportMaterialSpeed?: number
supportMaterialThreshold?: number
temperature?: number
thinWalls?: boolean
threads?: number
toolchangeGCode?: boolean
toolchangeGcode?: boolean
topInfillExtrusionWidth?: number | string
topSolidInfillSpeed?: number
topSolidLayers?: number
travelSpeed?: number
useFirmwareRetraction?: boolean
useRelativeEDistances?: boolean
useVolumetricE?: boolean
vibrationLimit?: number
wipe?: boolean
zOffset?: number
}
export type SlicerOptions = IOptionsBase & ISlic3rCLIOptions

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>
ExportHTML
</name>
</assembly>
<members>
<member name="T:ExportHTML.My.Resources.Resources">
<summary>
A strongly-typed resource class, for looking up localized strings, etc.
</summary>
</member>
<member name="P:ExportHTML.My.Resources.Resources.ResourceManager">
<summary>
Returns the cached ResourceManager instance used by this class.
</summary>
</member>
<member name="P:ExportHTML.My.Resources.Resources.Culture">
<summary>
Overrides the current thread's CurrentUICulture property for all
resource lookups using this strongly typed resource class.
</summary>
</member>
</members>
</doc>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,481 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v3.1",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v3.1": {
"bom/1.0.0": {
"dependencies": {
"CommandLineParser": "2.8.0",
"Jering.Javascript.NodeJS": "6.0.1",
"Newtonsoft.Json": "13.0.1",
"Xarial.XCad.SolidWorks": "0.7.4"
},
"runtime": {
"bom.dll": {}
}
},
"CommandLineParser/2.8.0": {
"runtime": {
"lib/netstandard2.0/CommandLine.dll": {
"assemblyVersion": "2.8.0.0",
"fileVersion": "2.8.0.0"
}
}
},
"Jering.Javascript.NodeJS/6.0.1": {
"dependencies": {
"Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0",
"Microsoft.Extensions.DependencyInjection": "5.0.1",
"Microsoft.Extensions.Http": "5.0.0",
"Microsoft.Extensions.Logging": "5.0.0",
"Microsoft.Extensions.Options": "5.0.0",
"System.Text.Encodings.Web": "5.0.1",
"System.Text.Json": "5.0.2"
},
"runtime": {
"lib/netcoreapp3.1/Jering.Javascript.NodeJS.dll": {
"assemblyVersion": "1.0.0.0",
"fileVersion": "1.0.0.0"
}
}
},
"Microsoft.AspNetCore.Hosting.Abstractions/2.2.0": {
"dependencies": {
"Microsoft.AspNetCore.Hosting.Server.Abstractions": "2.2.0",
"Microsoft.AspNetCore.Http.Abstractions": "2.2.0",
"Microsoft.Extensions.Hosting.Abstractions": "2.2.0"
},
"runtime": {
"lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll": {
"assemblyVersion": "2.2.0.0",
"fileVersion": "2.2.0.18316"
}
}
},
"Microsoft.AspNetCore.Hosting.Server.Abstractions/2.2.0": {
"dependencies": {
"Microsoft.AspNetCore.Http.Features": "2.2.0",
"Microsoft.Extensions.Configuration.Abstractions": "2.2.0"
},
"runtime": {
"lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll": {
"assemblyVersion": "2.2.0.0",
"fileVersion": "2.2.0.18316"
}
}
},
"Microsoft.AspNetCore.Http.Abstractions/2.2.0": {
"dependencies": {
"Microsoft.AspNetCore.Http.Features": "2.2.0",
"System.Text.Encodings.Web": "5.0.1"
},
"runtime": {
"lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll": {
"assemblyVersion": "2.2.0.0",
"fileVersion": "2.2.0.18316"
}
}
},
"Microsoft.AspNetCore.Http.Features/2.2.0": {
"dependencies": {
"Microsoft.Extensions.Primitives": "5.0.0"
},
"runtime": {
"lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll": {
"assemblyVersion": "2.2.0.0",
"fileVersion": "2.2.0.18316"
}
}
},
"Microsoft.Extensions.Configuration.Abstractions/2.2.0": {
"dependencies": {
"Microsoft.Extensions.Primitives": "5.0.0"
},
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll": {
"assemblyVersion": "2.2.0.0",
"fileVersion": "2.2.0.18315"
}
}
},
"Microsoft.Extensions.DependencyInjection/5.0.1": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0"
},
"runtime": {
"lib/netstandard2.1/Microsoft.Extensions.DependencyInjection.dll": {
"assemblyVersion": "5.0.0.1",
"fileVersion": "5.0.120.57516"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Microsoft.Extensions.FileProviders.Abstractions/2.2.0": {
"dependencies": {
"Microsoft.Extensions.Primitives": "5.0.0"
},
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": {
"assemblyVersion": "2.2.0.0",
"fileVersion": "2.2.0.18315"
}
}
},
"Microsoft.Extensions.Hosting.Abstractions/2.2.0": {
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "2.2.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
"Microsoft.Extensions.FileProviders.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging.Abstractions": "5.0.0"
},
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll": {
"assemblyVersion": "2.2.0.0",
"fileVersion": "2.2.0.18316"
}
}
},
"Microsoft.Extensions.Http/5.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
"Microsoft.Extensions.Logging": "5.0.0",
"Microsoft.Extensions.Logging.Abstractions": "5.0.0",
"Microsoft.Extensions.Options": "5.0.0"
},
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.Http.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Microsoft.Extensions.Logging/5.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "5.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
"Microsoft.Extensions.Logging.Abstractions": "5.0.0",
"Microsoft.Extensions.Options": "5.0.0",
"System.Diagnostics.DiagnosticSource": "5.0.0"
},
"runtime": {
"lib/netstandard2.1/Microsoft.Extensions.Logging.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Microsoft.Extensions.Logging.Abstractions/5.0.0": {
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Microsoft.Extensions.Options/5.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
"Microsoft.Extensions.Primitives": "5.0.0"
},
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.Options.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Microsoft.Extensions.Primitives/5.0.0": {
"runtime": {
"lib/netcoreapp3.0/Microsoft.Extensions.Primitives.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Newtonsoft.Json/13.0.1": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.1.25517"
}
}
},
"System.Diagnostics.DiagnosticSource/5.0.0": {
"runtime": {
"lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"System.Runtime.CompilerServices.Unsafe/5.0.0": {
"runtime": {
"lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"System.Text.Encodings.Web/5.0.1": {
"runtime": {
"lib/netcoreapp3.0/System.Text.Encodings.Web.dll": {
"assemblyVersion": "5.0.0.1",
"fileVersion": "5.0.421.11614"
}
}
},
"System.Text.Json/5.0.2": {
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "5.0.0",
"System.Text.Encodings.Web": "5.0.1"
},
"runtime": {
"lib/netcoreapp3.0/System.Text.Json.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.521.16609"
}
}
},
"Xarial.XCad/0.7.4": {
"runtime": {
"lib/netstandard2.1/Xarial.XCad.dll": {
"assemblyVersion": "0.7.4.0",
"fileVersion": "0.7.4.0"
}
}
},
"Xarial.XCad.SolidWorks/0.7.4": {
"dependencies": {
"Xarial.XCad": "0.7.4",
"Xarial.XCad.SolidWorks.Interops": "0.3.0",
"Xarial.XCad.Toolkit": "0.7.4"
},
"runtime": {
"lib/netcoreapp3.1/Xarial.XCad.SolidWorks.dll": {
"assemblyVersion": "0.7.4.0",
"fileVersion": "0.7.4.0"
}
}
},
"Xarial.XCad.SolidWorks.Interops/0.3.0": {
"runtime": {
"lib/net40/SolidWorks.Interop.sldworks.dll": {
"assemblyVersion": "28.1.0.74",
"fileVersion": "28.1.0.74"
},
"lib/net40/SolidWorks.Interop.swconst.dll": {
"assemblyVersion": "28.1.0.74",
"fileVersion": "28.1.0.74"
},
"lib/net40/SolidWorks.Interop.swpublished.dll": {
"assemblyVersion": "28.1.0.74",
"fileVersion": "28.1.0.74"
}
}
},
"Xarial.XCad.Toolkit/0.7.4": {
"dependencies": {
"Xarial.XCad": "0.7.4"
},
"runtime": {
"lib/netstandard2.1/Xarial.XCad.Toolkit.dll": {
"assemblyVersion": "0.7.4.0",
"fileVersion": "0.7.4.0"
}
}
}
}
},
"libraries": {
"bom/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"CommandLineParser/2.8.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-eco2HlKQBY4Joz9odHigzGpVzv6pjsXnY5lziioMveQxr+i2Z7xYcIOMeZTgYiqnMtMAbXMXsVhrNfWO5vJS8Q==",
"path": "commandlineparser/2.8.0",
"hashPath": "commandlineparser.2.8.0.nupkg.sha512"
},
"Jering.Javascript.NodeJS/6.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Dg0YRu0EtyWwLM7hrp6jJg5kQE0LIr44bu7XQzyaSWQfb8Nfzvcv/SduQOu1fz/52SCKZp2H873QClO0SUjJzg==",
"path": "jering.javascript.nodejs/6.0.1",
"hashPath": "jering.javascript.nodejs.6.0.1.nupkg.sha512"
},
"Microsoft.AspNetCore.Hosting.Abstractions/2.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ubycklv+ZY7Kutdwuy1W4upWcZ6VFR8WUXU7l7B2+mvbDBBPAcfpi+E+Y5GFe+Q157YfA3C49D2GCjAZc7Mobw==",
"path": "microsoft.aspnetcore.hosting.abstractions/2.2.0",
"hashPath": "microsoft.aspnetcore.hosting.abstractions.2.2.0.nupkg.sha512"
},
"Microsoft.AspNetCore.Hosting.Server.Abstractions/2.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-1PMijw8RMtuQF60SsD/JlKtVfvh4NORAhF4wjysdABhlhTrYmtgssqyncR0Stq5vqtjplZcj6kbT4LRTglt9IQ==",
"path": "microsoft.aspnetcore.hosting.server.abstractions/2.2.0",
"hashPath": "microsoft.aspnetcore.hosting.server.abstractions.2.2.0.nupkg.sha512"
},
"Microsoft.AspNetCore.Http.Abstractions/2.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==",
"path": "microsoft.aspnetcore.http.abstractions/2.2.0",
"hashPath": "microsoft.aspnetcore.http.abstractions.2.2.0.nupkg.sha512"
},
"Microsoft.AspNetCore.Http.Features/2.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==",
"path": "microsoft.aspnetcore.http.features/2.2.0",
"hashPath": "microsoft.aspnetcore.http.features.2.2.0.nupkg.sha512"
},
"Microsoft.Extensions.Configuration.Abstractions/2.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==",
"path": "microsoft.extensions.configuration.abstractions/2.2.0",
"hashPath": "microsoft.extensions.configuration.abstractions.2.2.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection/5.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-//mDNrYeiJ0eh/awFhDFJQzkRVra/njU5Y4fyK7X29g5HScrzbUkKOKlyTtygthcGFt4zNC8G5CFCjb/oizomA==",
"path": "microsoft.extensions.dependencyinjection/5.0.1",
"hashPath": "microsoft.extensions.dependencyinjection.5.0.1.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==",
"path": "microsoft.extensions.dependencyinjection.abstractions/5.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.5.0.0.nupkg.sha512"
},
"Microsoft.Extensions.FileProviders.Abstractions/2.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-EcnaSsPTqx2MGnHrmWOD0ugbuuqVT8iICqSqPzi45V5/MA1LjUNb0kwgcxBGqizV1R+WeBK7/Gw25Jzkyk9bIw==",
"path": "microsoft.extensions.fileproviders.abstractions/2.2.0",
"hashPath": "microsoft.extensions.fileproviders.abstractions.2.2.0.nupkg.sha512"
},
"Microsoft.Extensions.Hosting.Abstractions/2.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-+k4AEn68HOJat5gj1TWa6X28WlirNQO9sPIIeQbia+91n03esEtMSSoekSTpMjUzjqtJWQN3McVx0GvSPFHF/Q==",
"path": "microsoft.extensions.hosting.abstractions/2.2.0",
"hashPath": "microsoft.extensions.hosting.abstractions.2.2.0.nupkg.sha512"
},
"Microsoft.Extensions.Http/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-kT1ijDKZuSUhBtYoC1sXrmVKP7mA08h9Xrsr4VrS/QOtiKCEtUTTd7dd3XI9dwAb46tZSak13q/zdIcr4jqbyg==",
"path": "microsoft.extensions.http/5.0.0",
"hashPath": "microsoft.extensions.http.5.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Logging/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-MgOwK6tPzB6YNH21wssJcw/2MKwee8b2gI7SllYfn6rvTpIrVvVS5HAjSU2vqSku1fwqRvWP0MdIi14qjd93Aw==",
"path": "microsoft.extensions.logging/5.0.0",
"hashPath": "microsoft.extensions.logging.5.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Logging.Abstractions/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-NxP6ahFcBnnSfwNBi2KH2Oz8Xl5Sm2krjId/jRR3I7teFphwiUoUeZPwTNA21EX+5PtjqmyAvKaOeBXcJjcH/w==",
"path": "microsoft.extensions.logging.abstractions/5.0.0",
"hashPath": "microsoft.extensions.logging.abstractions.5.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Options/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-CBvR92TCJ5uBIdd9/HzDSrxYak+0W/3+yxrNg8Qm6Bmrkh5L+nu6m3WeazQehcZ5q1/6dDA7J5YdQjim0165zg==",
"path": "microsoft.extensions.options/5.0.0",
"hashPath": "microsoft.extensions.options.5.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Primitives/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cI/VWn9G1fghXrNDagX9nYaaB/nokkZn0HYAawGaELQrl8InSezfe9OnfPZLcJq3esXxygh3hkq2c3qoV3SDyQ==",
"path": "microsoft.extensions.primitives/5.0.0",
"hashPath": "microsoft.extensions.primitives.5.0.0.nupkg.sha512"
},
"Newtonsoft.Json/13.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
"path": "newtonsoft.json/13.0.1",
"hashPath": "newtonsoft.json.13.0.1.nupkg.sha512"
},
"System.Diagnostics.DiagnosticSource/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==",
"path": "system.diagnostics.diagnosticsource/5.0.0",
"hashPath": "system.diagnostics.diagnosticsource.5.0.0.nupkg.sha512"
},
"System.Runtime.CompilerServices.Unsafe/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==",
"path": "system.runtime.compilerservices.unsafe/5.0.0",
"hashPath": "system.runtime.compilerservices.unsafe.5.0.0.nupkg.sha512"
},
"System.Text.Encodings.Web/5.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==",
"path": "system.text.encodings.web/5.0.1",
"hashPath": "system.text.encodings.web.5.0.1.nupkg.sha512"
},
"System.Text.Json/5.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==",
"path": "system.text.json/5.0.2",
"hashPath": "system.text.json.5.0.2.nupkg.sha512"
},
"Xarial.XCad/0.7.4": {
"type": "package",
"serviceable": true,
"sha512": "sha512-HkTqmme828wJ/RDFnfieWZkvTdXFBdO2p6trMUiDa4P9P5C7gLfXslfG5q2CZ9d4vhSXjfweWSrp1ucSBqM6sg==",
"path": "xarial.xcad/0.7.4",
"hashPath": "xarial.xcad.0.7.4.nupkg.sha512"
},
"Xarial.XCad.SolidWorks/0.7.4": {
"type": "package",
"serviceable": true,
"sha512": "sha512-n2UP+Z/5Sxrt64taarHXqerp3sKYLv4f+CBmkxKAuFFX/8McxXiJIud0iRwfGeIEOxrd0iYlNQXxzy3JVJiMWQ==",
"path": "xarial.xcad.solidworks/0.7.4",
"hashPath": "xarial.xcad.solidworks.0.7.4.nupkg.sha512"
},
"Xarial.XCad.SolidWorks.Interops/0.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ebiAAOHtYpZfjbO4PgdWcmv8+YtoPfAJwUJBeUM2SEBxO1+ru2dYI0XI2Fa1MpmAlsDRuBj6Z3LKtZMtff6NEQ==",
"path": "xarial.xcad.solidworks.interops/0.3.0",
"hashPath": "xarial.xcad.solidworks.interops.0.3.0.nupkg.sha512"
},
"Xarial.XCad.Toolkit/0.7.4": {
"type": "package",
"serviceable": true,
"sha512": "sha512-xJKIqEzdZKst+KmDZl1EvIsUebEEBa1yVmH2GpKHYQJkDhAakBfRQYO4/70/z3Qi/WmrNULq95F+lYc4mUoOTQ==",
"path": "xarial.xcad.toolkit/0.7.4",
"hashPath": "xarial.xcad.toolkit.0.7.4.nupkg.sha512"
}
}
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,8 @@
{
"runtimeOptions": {
"additionalProbingPaths": [
"C:\\Users\\mc007\\.dotnet\\store\\|arch|\\|tfm|",
"C:\\Users\\mc007\\.nuget\\packages"
]
}
}

View File

@ -0,0 +1,9 @@
{
"runtimeOptions": {
"tfm": "netcoreapp3.1",
"framework": {
"name": "Microsoft.WindowsDesktop.App",
"version": "3.1.0"
}
}
}

View File

@ -0,0 +1,5 @@
SET inputFilePath=%1
SET outFilePath=%2
SET view=%3
PowerShell -NoProfile -ExecutionPolicy Bypass -File "%~dp0export.ps1" %inputFilePath% %outFilePath% %view%

View File

@ -0,0 +1,108 @@
using SolidWorks.Interop.sldworks;
using System;
namespace CodeStack
{
public static class Exporter
{
#region Libraries
static Exporter()
{
AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
}
public static void LoadLibrary(params object[] libs)
{
foreach(string lib in libs)
{
Console.WriteLine(string.Format("Loading library: {0}", lib));
System.Reflection.Assembly assm = System.Reflection.Assembly.LoadFrom(lib);
Console.WriteLine(assm.GetName().ToString());
}
}
private static System.Reflection.Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
{
foreach (System.Reflection.Assembly assm in AppDomain.CurrentDomain.GetAssemblies())
{
if(assm.GetName().ToString() == args.Name)
{
return assm;
}
};
return null;
}
#endregion
public static void ExportFile(string filePath, string outFilePath, string view)
{
Console.WriteLine("Connecting to SOLIDWORKS...");
ISldWorks app = Activator.CreateInstance(Type.GetTypeFromProgID("SldWorks.Application")) as ISldWorks;
if (app != null)
{
Console.WriteLine(string.Format("Opening file '{0}'...", filePath));
IDocumentSpecification docSpec = app.GetOpenDocSpec(filePath) as IDocumentSpecification;
docSpec.ReadOnly = true;
docSpec.Silent = true;
IModelDoc2 model = app.OpenDoc7(docSpec);
model.ShowNamedView2(view,-1);
model.ViewZoomtofit2();
int swViewDisplayHideAllTypes = 198;
model.SetUserPreferenceToggle(swViewDisplayHideAllTypes, true);
// DrawingDoc swDraw = model as DrawingDoc;
// View swView = swDraw.GetFirstView() as View;
// view.SetDisplayMode3(False, 4, false, false);
/*Member Description
swDisplayModeDEFAULT 8
swDisplayModeUNKNOWN -1
swFACETED_HIDDEN 6
swFACETED_HIDDEN_GREYED 5
swFACETED_WIREFRAME 4
swHIDDEN 2; Hidden Lines Removed (HLR)
swHIDDEN_GREYED 1; Hidden Lines Visible (HLV)
swSHADED 3
swSHADED_EDGES 7
swWIREFRAME 0
*/
if (model != null)
{
const int swSaveAsCurrentVersion = 0;
const int swSaveAsOptions_Silent = 1;
int err = -1;
int warn = -1;
Console.WriteLine(string.Format("Exporting file '{0}' to '{1}'...", filePath, outFilePath));
if (!model.Extension.SaveAs(outFilePath, swSaveAsCurrentVersion,
swSaveAsOptions_Silent, null, ref err, ref warn))
{
Console.WriteLine(string.Format("Failed to export '{0}' to '{1}'. Error code: {2}", filePath, outFilePath, err));
}
Console.WriteLine(string.Format("Closing file '{0}'...", filePath));
app.CloseDoc(model.GetTitle());
}
else
{
Console.WriteLine(string.Format("Failed to open document: '{0}'. Error code: {1}",filePath, docSpec.Error));
}
}
else
{
Console.WriteLine("Failed to connect to SOLIDWORKS instance");
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More