Merge branch 'dev' into feature-multicolor-wires

# Conflicts:
#	.gitignore
#	src/batch.py
#	src/wireviz.py
#	src/wv_colors.py
This commit is contained in:
Andrew Katz 2020-07-01 12:54:24 -04:00
commit f0d10bfef8
90 changed files with 2727 additions and 7536 deletions

25
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: Create Examples
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
max-parallel: 4
matrix:
python-version: [3.7, 3.8]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Setup Graphviz
uses: ts-graphviz/setup-graphviz@v1
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .
- name: Create Examples
run: PYTHONPATH=$(pwd)/src:$PYTHONPATH cd src/wireviz/ && python build_examples.py

14
.gitignore vendored
View File

@ -1,12 +1,18 @@
.DS_Store
temp/
.eggs
__pycache__
.*.swp
*.egg-info
*.pyc
build
data
dist
venv/
.vscode/
.idea/
# IntelliJ Stuff
# Jetbrains Stuff
# Default ignored files
/shelf/
/workspace.xml

View File

@ -17,13 +17,25 @@ WireViz is a tool for easily documenting cables, wiring harnesses and connector
* [DIN 47100](https://en.wikipedia.org/wiki/DIN_47100) (WT/BN/GN/YE/GY/PK/BU/RD/BK/VT/...)
* [IEC 62](https://en.wikipedia.org/wiki/Electronic_color_code#Color_band_system) (BN/RD/OR/YE/GN/BU/VT/GY/WT/BK/...)
* Understands wire gauge in mm² or AWG
* Optionally auto-calculates and displays AWG equivalent when specifying mm²
* Optionally auto-calculates equivalent gauge between mm² and AWG
* Allows more than one connector per side, as well as loopbacks
* Allows for easy-autorouting for 1-to-1 wiring
* Generates BOM (Bill of Materials)
_Note_: WireViz is not designed to represent the complete wiring of a system. Its main aim is to document the construction of individual wires and harnesses.
## Installation
WireWiz requires GraphViz to be installed in order to work. See the [GraphViz download page](https://graphviz.org/download/) for OS-specific instructions.
Installation of the WireWiz package and its Python dependencies can be done using pip after cloning the repository:
```
git clone <repo url>
cd <working copy>
pip3 install -e .
```
## Examples
### Demo 01
@ -51,7 +63,7 @@ cables:
connections:
-
- X1: [5,2,1]
- X1: [5,2,3]
- W1: [1,2,3]
- X2: [1,3,2]
-
@ -79,7 +91,7 @@ as well as the [example gallery](examples/readme.md) to see more of what WireViz
## Usage
```
$ python3 wireviz.py ~/path/to/file/mywire.yml
$ wireviz ~/path/to/file/mywire.yml
```
This will output the following files
@ -92,14 +104,23 @@ mywire.bom.tsv BOM (bill of materials) as tab-separated text file
mywire.html HTML page with wiring diagram and BOM embedded
```
### (Re-)Building the example projects
If you would like to rebuild all of the included demos, examples and tutorials, use the ```build_examples.py``` script:
```cd src/wireviz
./build_examples.py
```
## Status
This is very much a [work in progress](todo.md). Source code, API, syntax and functionality may change wildly at any time.
This is very much a [work in progress](https://github.com/formatc1702/WireViz/projects/1). Source code, API, syntax and functionality may change wildly at any time.
## Requirements
Developed and tested using Python 3.7; might not work with older Python versions.
Ubuntu 18.04 LTS users in particular may need to separately install Python 3.7 or above, as that comes with Python 3.6 as the included system Python install.
## License
GNU GPLv3

View File

@ -1,4 +1,4 @@
Item Qty Unit Designators
D-Sub, female, 9 pins 1 X1
Molex KK 254, female, 3 pins 1 X2
Cable 3 x 0.25 mm² shielded 0.2 m W1
Connector, D-Sub, female, 9 pins 1 X1
Connector, Molex KK 254, female, 3 pins 1 X2
Cable, 3 x 0.25 mm² shielded 0.2 m W1

1 Item Qty Unit Designators
2 D-Sub, female, 9 pins Connector, D-Sub, female, 9 pins 1 X1
3 Molex KK 254, female, 3 pins Connector, Molex KK 254, female, 3 pins 1 X2
4 Cable 3 x 0.25 mm² shielded Cable, 3 x 0.25 mm² shielded 0.2 m W1

View File

@ -13,9 +13,9 @@ graph {
X1:p2r:e -- W1:w2:w
W1:w2:e -- X2:p3l:w
edge [color="#000000:#00ff00:#000000"]
X1:p1r:e -- W1:w3:w
X1:p3r:e -- W1:w3:w
W1:w3:e -- X2:p2l:w
edge [color="#000000"]
X1:p5r:e -- W1:ws:w
W1 [label=<<table border="0" cellspacing="0" cellpadding="0"><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td colspan="4">W1</td></tr><tr><td>3x</td><td>0.25 mm²</td><td>+ S</td><td>0.2 m</td></tr></table></td></tr><tr><td>&nbsp;</td></tr><tr><td><table border="0" cellspacing="0" cellborder="0"><tr><td>X1:5</td><td>WH</td><td>X2:1</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ffffff" border="2" sides="tb" port="w1"></td></tr><tr><td>X1:2</td><td>BN</td><td>X2:3</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#666600" border="2" sides="tb" port="w2"></td></tr><tr><td>X1:1</td><td>GN</td><td>X2:2</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#00ff00" border="2" sides="tb" port="w3"></td></tr><tr><td>&nbsp;</td></tr><tr><td>X1:5</td><td>Shield</td><td><!-- s_out --></td></tr><tr><td colspan="3" cellpadding="0" height="6" border="2" sides="b" port="ws"></td></tr><tr><td>&nbsp;</td></tr></table></td></tr></table>> fillcolor=white margin=0 shape=box style=""]
W1 [label=<<table border="0" cellspacing="0" cellpadding="0"><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td colspan="4">W1</td></tr><tr><td>3x</td><td>0.25 mm²</td><td>+ S</td><td>0.2 m</td></tr></table></td></tr><tr><td>&nbsp;</td></tr><tr><td><table border="0" cellspacing="0" cellborder="0"><tr><td>X1:5</td><td>WH</td><td>X2:1</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ffffff" border="2" sides="tb" port="w1"></td></tr><tr><td>X1:2</td><td>BN</td><td>X2:3</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#666600" border="2" sides="tb" port="w2"></td></tr><tr><td>X1:3</td><td>GN</td><td>X2:2</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#00ff00" border="2" sides="tb" port="w3"></td></tr><tr><td>&nbsp;</td></tr><tr><td>X1:5</td><td>Shield</td><td><!-- s_out --></td></tr><tr><td colspan="3" cellpadding="0" height="6" border="2" sides="b" port="ws"></td></tr><tr><td>&nbsp;</td></tr></table></td></tr></table>> fillcolor=white margin=0 shape=box style=""]
}

View File

@ -83,7 +83,7 @@
<polygon fill="#666600" stroke="transparent" stroke-width="2" points="304.5,-170 304.5,-176 467.5,-176 467.5,-170 304.5,-170"/>
<polyline fill="none" stroke="black" stroke-width="2" points="305.5,-171 466.5,-171 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="466.5,-175 305.5,-175 "/>
<text text-anchor="start" x="316" y="-156.8" font-family="arial" font-size="14.00">X1:1</text>
<text text-anchor="start" x="316" y="-156.8" font-family="arial" font-size="14.00">X1:3</text>
<text text-anchor="start" x="375" y="-156.8" font-family="arial" font-size="14.00">GN</text>
<text text-anchor="start" x="428" y="-156.8" font-family="arial" font-size="14.00">X2:2</text>
<polygon fill="#00ff00" stroke="transparent" stroke-width="2" points="304.5,-145 304.5,-151 467.5,-151 467.5,-145 304.5,-145"/>
@ -112,9 +112,9 @@
<!-- X1&#45;&#45;W1 -->
<g id="edge5" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M160,-194C225.5,-195.63 234.57,-147.63 304,-146"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M160,-196C227.46,-196 236.54,-148 304,-148"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M160,-198C229.43,-196.37 238.5,-148.37 304,-150"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M160,-148C223.76,-148.02 239.75,-146.02 304,-146"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M160,-150C224.01,-150 239.99,-148 304,-148"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M160,-152C224.25,-151.98 240.24,-149.98 304,-150"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge7" class="edge">
@ -168,4 +168,4 @@
</g>
</g>
</svg>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">D-Sub, female, 9 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Molex KK 254, female, 3 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Cable 3 x 0.25 mm² shielded</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr></table></body></html>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, D-Sub, female, 9 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Molex KK 254, female, 3 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Cable, 3 x 0.25 mm² shielded</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr></table></body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -83,7 +83,7 @@
<polygon fill="#666600" stroke="transparent" stroke-width="2" points="304.5,-170 304.5,-176 467.5,-176 467.5,-170 304.5,-170"/>
<polyline fill="none" stroke="black" stroke-width="2" points="305.5,-171 466.5,-171 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="466.5,-175 305.5,-175 "/>
<text text-anchor="start" x="316" y="-156.8" font-family="arial" font-size="14.00">X1:1</text>
<text text-anchor="start" x="316" y="-156.8" font-family="arial" font-size="14.00">X1:3</text>
<text text-anchor="start" x="375" y="-156.8" font-family="arial" font-size="14.00">GN</text>
<text text-anchor="start" x="428" y="-156.8" font-family="arial" font-size="14.00">X2:2</text>
<polygon fill="#00ff00" stroke="transparent" stroke-width="2" points="304.5,-145 304.5,-151 467.5,-151 467.5,-145 304.5,-145"/>
@ -112,9 +112,9 @@
<!-- X1&#45;&#45;W1 -->
<g id="edge5" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M160,-194C225.5,-195.63 234.57,-147.63 304,-146"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M160,-196C227.46,-196 236.54,-148 304,-148"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M160,-198C229.43,-196.37 238.5,-148.37 304,-150"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M160,-148C223.76,-148.02 239.75,-146.02 304,-146"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M160,-150C224.01,-150 239.99,-148 304,-148"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M160,-152C224.25,-151.98 240.24,-149.98 304,-150"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge7" class="edge">

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -18,7 +18,7 @@ cables:
connections:
-
- X1: [5,2,1]
- X1: [5,2,3]
- W1: [1,2,3]
- X2: [1,3,2]
-

View File

@ -1,13 +1,13 @@
Item Qty Unit Designators
Crimp ferrule, 0.25 mm² 2
Molex KK 254, female, 4 pins 2 X2, X3
Molex KK 254, female, 5 pins 1 X4
Molex KK 254, female, 8 pins 1 X1
Cable 2 x 0.25 mm² 0.3 m W4
Wire 0.14 mm² BK 0.9 m W1, W2, W3
Wire 0.14 mm² BU 0.3 m W3
Wire 0.14 mm² GN 0.6 m W1, W2
Wire 0.14 mm² OG 0.3 m W3
Wire 0.14 mm² RD 0.6 m W1, W2
Wire 0.14 mm² VT 0.3 m W3
Wire 0.14 mm² YE 0.6 m W1, W2
Connector, Crimp ferrule, 0.25 mm² 2
Connector, Molex KK 254, female, 4 pins 2 X2, X3
Connector, Molex KK 254, female, 5 pins 1 X4
Connector, Molex KK 254, female, 8 pins 1 X1
Cable, 2 x 0.25 mm² 0.3 m W4
Wire, 0.14 mm², BK 0.9 m W1, W2, W3
Wire, 0.14 mm², BU 0.3 m W3
Wire, 0.14 mm², GN 0.6 m W1, W2
Wire, 0.14 mm², OG 0.3 m W3
Wire, 0.14 mm², RD 0.6 m W1, W2
Wire, 0.14 mm², VT 0.3 m W3
Wire, 0.14 mm², YE 0.6 m W1, W2

1 Item Qty Unit Designators
2 Crimp ferrule, 0.25 mm² Connector, Crimp ferrule, 0.25 mm² 2
3 Molex KK 254, female, 4 pins Connector, Molex KK 254, female, 4 pins 2 X2, X3
4 Molex KK 254, female, 5 pins Connector, Molex KK 254, female, 5 pins 1 X4
5 Molex KK 254, female, 8 pins Connector, Molex KK 254, female, 8 pins 1 X1
6 Cable 2 x 0.25 mm² Cable, 2 x 0.25 mm² 0.3 m W4
7 Wire 0.14 mm² BK Wire, 0.14 mm², BK 0.9 m W1, W2, W3
8 Wire 0.14 mm² BU Wire, 0.14 mm², BU 0.3 m W3
9 Wire 0.14 mm² GN Wire, 0.14 mm², GN 0.6 m W1, W2
10 Wire 0.14 mm² OG Wire, 0.14 mm², OG 0.3 m W3
11 Wire 0.14 mm² RD Wire, 0.14 mm², RD 0.6 m W1, W2
12 Wire 0.14 mm² VT Wire, 0.14 mm², VT 0.3 m W3
13 Wire 0.14 mm² YE Wire, 0.14 mm², YE 0.6 m W1, W2

View File

@ -8,7 +8,7 @@ graph {
X2 [label="X2|{Molex KK 254|female|4-pin}|{{<p1l>1|<p2l>2|<p3l>3|<p4l>4}|{GND|+5V|SCL|SDA}}"]
X3 [label="X3|{Molex KK 254|female|4-pin}|{{<p1l>1|<p2l>2|<p3l>3|<p4l>4}|{GND|+5V|SCL|SDA}}"]
X4 [label="X4|{Molex KK 254|female|5-pin}|{{<p1l>1|<p2l>2|<p3l>3|<p4l>4|<p5l>5}|{GND|+12V|MISO|MOSI|SCK}}"]
F1 [label=<
_F1 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> Crimp ferrule, 0.25 mm² </TD>
@ -18,7 +18,7 @@ graph {
> margin=0 orientation=180 shape=none style=filled]
F2 [label=<
_F2 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> Crimp ferrule, 0.25 mm² </TD>
@ -68,9 +68,9 @@ graph {
W3:w4:e -- X4:p5l:w
W3 [label=<<table border="0" cellspacing="0" cellpadding="0"><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td colspan="3">W3</td></tr><tr><td>4x</td><td>0.14 mm² (26 AWG)</td><td>0.3 m</td></tr></table></td></tr><tr><td>&nbsp;</td></tr><tr><td><table border="0" cellspacing="0" cellborder="0"><tr><td>X1:1</td><td>BK</td><td>X4:1</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#000000" border="2" sides="tb" port="w1"></td></tr><tr><td>X1:5</td><td>BU</td><td>X4:3</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#0066ff" border="2" sides="tb" port="w2"></td></tr><tr><td>X1:6</td><td>OG</td><td>X4:4</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ff8000" border="2" sides="tb" port="w3"></td></tr><tr><td>X1:7</td><td>VT</td><td>X4:5</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#8000ff" border="2" sides="tb" port="w4"></td></tr><tr><td>&nbsp;</td></tr></table></td></tr></table>> fillcolor=white margin=0 shape=box style="filled,dashed"]
edge [color="#000000:#000000:#000000"]
F1:e -- W4:w1:w
_F1:e -- W4:w1:w
edge [color="#000000:#ff0000:#000000"]
F2:e -- W4:w2:w
_F2:e -- W4:w2:w
edge [color="#000000:#000000:#000000"]
W4:w1:e -- X4:p1l:w
edge [color="#000000:#ff0000:#000000"]

View File

@ -341,9 +341,9 @@
<polyline fill="none" stroke="black" points="780,-191.5 898,-191.5 "/>
<text text-anchor="middle" x="839" y="-176.3" font-family="arial" font-size="14.00">SCK</text>
</g>
<!-- F1 -->
<!-- _F1 -->
<g id="node5" class="node">
<title>F1</title>
<title>_F1</title>
<polygon fill="white" stroke="transparent" points="15.5,-60 190.5,-60 190.5,-96 15.5,-96 15.5,-60"/>
<text text-anchor="start" x="19" y="-73.8" font-family="arial" font-size="14.00"> Crimp ferrule, 0.25 mm² &#160;</text>
<text text-anchor="start" x="180" y="-73.8" font-family="arial" font-size="14.00"> &#160;</text>
@ -374,24 +374,24 @@
<polyline fill="none" stroke="black" stroke-width="2" points="547,-37 351,-37 "/>
<text text-anchor="start" x="374.5" y="-18.8" font-family="arial" font-size="14.00"> </text>
</g>
<!-- F1&#45;&#45;W4 -->
<!-- _F1&#45;&#45;W4 -->
<g id="edge25" class="edge">
<title>F1:e&#45;&#45;W4:w</title>
<title>_F1:e&#45;&#45;W4:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M192,-76C261.21,-76.64 277.86,-58.64 350,-58"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M192,-78C262.68,-78 279.32,-60 350,-60"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M192,-80C264.14,-79.36 280.79,-61.36 350,-62"/>
</g>
<!-- F2 -->
<!-- _F2 -->
<g id="node6" class="node">
<title>F2</title>
<title>_F2</title>
<polygon fill="white" stroke="transparent" points="15.5,0 190.5,0 190.5,-36 15.5,-36 15.5,0"/>
<text text-anchor="start" x="19" y="-13.8" font-family="arial" font-size="14.00"> Crimp ferrule, 0.25 mm² &#160;</text>
<text text-anchor="start" x="180" y="-13.8" font-family="arial" font-size="14.00"> &#160;</text>
<polygon fill="none" stroke="black" points="15.5,-7.5 15.5,-28.5 190.5,-28.5 190.5,-7.5 15.5,-7.5"/>
</g>
<!-- F2&#45;&#45;W4 -->
<!-- _F2&#45;&#45;W4 -->
<g id="edge26" class="edge">
<title>F2:e&#45;&#45;W4:w</title>
<title>_F2:e&#45;&#45;W4:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M192,-16C264.05,-16.6 280.8,-33.6 350,-33"/>
<path fill="none" stroke="#ff0000" stroke-width="2" d="M192,-18C262.63,-18 279.37,-35 350,-35"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M192,-20C261.2,-19.4 277.95,-36.4 350,-37"/>
@ -496,4 +496,4 @@
</g>
</g>
</svg>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Crimp ferrule, 0.25 mm²</td><td align="right" style="border:1px solid #000000; padding: 4px">2</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px"></td></tr><tr><td style="border:1px solid #000000; padding: 4px">Molex KK 254, female, 4 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">2</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X2, X3</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Molex KK 254, female, 5 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X4</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Molex KK 254, female, 8 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Cable 2 x 0.25 mm²</td><td align="right" style="border:1px solid #000000; padding: 4px">0.3</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W4</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.14 mm² BK</td><td align="right" style="border:1px solid #000000; padding: 4px">0.9</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2, W3</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.14 mm² BU</td><td align="right" style="border:1px solid #000000; padding: 4px">0.3</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W3</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.14 mm² GN</td><td align="right" style="border:1px solid #000000; padding: 4px">0.6</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.14 mm² OG</td><td align="right" style="border:1px solid #000000; padding: 4px">0.3</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W3</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.14 mm² RD</td><td align="right" style="border:1px solid #000000; padding: 4px">0.6</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.14 mm² VT</td><td align="right" style="border:1px solid #000000; padding: 4px">0.3</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W3</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.14 mm² YE</td><td align="right" style="border:1px solid #000000; padding: 4px">0.6</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr></table></body></html>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Crimp ferrule, 0.25 mm²</td><td align="right" style="border:1px solid #000000; padding: 4px">2</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px"></td></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Molex KK 254, female, 4 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">2</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X2, X3</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Molex KK 254, female, 5 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X4</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Molex KK 254, female, 8 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Cable, 2 x 0.25 mm²</td><td align="right" style="border:1px solid #000000; padding: 4px">0.3</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W4</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.14 mm², BK</td><td align="right" style="border:1px solid #000000; padding: 4px">0.9</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2, W3</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.14 mm², BU</td><td align="right" style="border:1px solid #000000; padding: 4px">0.3</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W3</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.14 mm², GN</td><td align="right" style="border:1px solid #000000; padding: 4px">0.6</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.14 mm², OG</td><td align="right" style="border:1px solid #000000; padding: 4px">0.3</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W3</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.14 mm², RD</td><td align="right" style="border:1px solid #000000; padding: 4px">0.6</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.14 mm², VT</td><td align="right" style="border:1px solid #000000; padding: 4px">0.3</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W3</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.14 mm², YE</td><td align="right" style="border:1px solid #000000; padding: 4px">0.6</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr></table></body></html>

View File

@ -341,9 +341,9 @@
<polyline fill="none" stroke="black" points="780,-191.5 898,-191.5 "/>
<text text-anchor="middle" x="839" y="-176.3" font-family="arial" font-size="14.00">SCK</text>
</g>
<!-- F1 -->
<!-- _F1 -->
<g id="node5" class="node">
<title>F1</title>
<title>_F1</title>
<polygon fill="white" stroke="transparent" points="15.5,-60 190.5,-60 190.5,-96 15.5,-96 15.5,-60"/>
<text text-anchor="start" x="19" y="-73.8" font-family="arial" font-size="14.00"> Crimp ferrule, 0.25 mm² &#160;</text>
<text text-anchor="start" x="180" y="-73.8" font-family="arial" font-size="14.00"> &#160;</text>
@ -374,24 +374,24 @@
<polyline fill="none" stroke="black" stroke-width="2" points="547,-37 351,-37 "/>
<text text-anchor="start" x="374.5" y="-18.8" font-family="arial" font-size="14.00"> </text>
</g>
<!-- F1&#45;&#45;W4 -->
<!-- _F1&#45;&#45;W4 -->
<g id="edge25" class="edge">
<title>F1:e&#45;&#45;W4:w</title>
<title>_F1:e&#45;&#45;W4:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M192,-76C261.21,-76.64 277.86,-58.64 350,-58"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M192,-78C262.68,-78 279.32,-60 350,-60"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M192,-80C264.14,-79.36 280.79,-61.36 350,-62"/>
</g>
<!-- F2 -->
<!-- _F2 -->
<g id="node6" class="node">
<title>F2</title>
<title>_F2</title>
<polygon fill="white" stroke="transparent" points="15.5,0 190.5,0 190.5,-36 15.5,-36 15.5,0"/>
<text text-anchor="start" x="19" y="-13.8" font-family="arial" font-size="14.00"> Crimp ferrule, 0.25 mm² &#160;</text>
<text text-anchor="start" x="180" y="-13.8" font-family="arial" font-size="14.00"> &#160;</text>
<polygon fill="none" stroke="black" points="15.5,-7.5 15.5,-28.5 190.5,-28.5 190.5,-7.5 15.5,-7.5"/>
</g>
<!-- F2&#45;&#45;W4 -->
<!-- _F2&#45;&#45;W4 -->
<g id="edge26" class="edge">
<title>F2:e&#45;&#45;W4:w</title>
<title>_F2:e&#45;&#45;W4:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M192,-16C264.05,-16.6 280.8,-33.6 350,-33"/>
<path fill="none" stroke="#ff0000" stroke-width="2" d="M192,-18C262.63,-18 279.37,-35 350,-35"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M192,-20C261.2,-19.4 277.95,-36.4 350,-37"/>

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,3 +1,3 @@
Item Qty Unit Designators
Molex KK 254, female, 4 pins 2 X1, X2
Cable 4 x 0.25 mm² shielded 0.2 m W1
Connector, Molex KK 254, female, 4 pins 2 X1, X2
Cable, 4 x 0.25 mm² shielded 0.2 m W1

1 Item Qty Unit Designators
2 Molex KK 254, female, 4 pins Connector, Molex KK 254, female, 4 pins 2 X1, X2
3 Cable 4 x 0.25 mm² shielded Cable, 4 x 0.25 mm² shielded 0.2 m W1

View File

@ -172,4 +172,4 @@
</g>
</g>
</svg>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Molex KK 254, female, 4 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">2</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1, X2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Cable 4 x 0.25 mm² shielded</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr></table></body></html>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Molex KK 254, female, 4 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">2</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1, X2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Cable, 4 x 0.25 mm² shielded</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr></table></body></html>

View File

@ -1,4 +1,5 @@
Item Qty Unit Designators
Molex Micro-Fit, female, 2 pins 3 X2, X3, X4
Molex Micro-Fit, male, 2 pins 1 X1
Cable 2 x 0.25 mm² 0.6 m W1, W2, W3
Connector, Molex Micro-Fit, female, 2 pins 3 X2, X3, X4
Connector, Molex Micro-Fit, male, 2 pins 1 X1
Cable, 2 x 0.25 mm² 0.4 m W1, W2
Cable, 2 x 20 AWG 0.2 m W3

1 Item Qty Unit Designators
2 Molex Micro-Fit, female, 2 pins Connector, Molex Micro-Fit, female, 2 pins 3 X2, X3, X4
3 Molex Micro-Fit, male, 2 pins Connector, Molex Micro-Fit, male, 2 pins 1 X1
4 Cable 2 x 0.25 mm² Cable, 2 x 0.25 mm² 0.6 0.4 m W1, W2, W3 W1, W2
5 Cable, 2 x 20 AWG 0.2 m W3

View File

@ -28,5 +28,5 @@ graph {
edge [color="#000000:#ff0000:#000000"]
X1:p2r:e -- W3:w2:w
W3:w2:e -- X4:p2l:w
W3 [label=<<table border="0" cellspacing="0" cellpadding="0"><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td colspan="3">W3</td></tr><tr><td>2x</td><td>0.25 mm² (24 AWG)</td><td>0.2 m</td></tr></table></td></tr><tr><td>&nbsp;</td></tr><tr><td><table border="0" cellspacing="0" cellborder="0"><tr><td>X1:1</td><td>BK</td><td>X4:1</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#000000" border="2" sides="tb" port="w1"></td></tr><tr><td>X1:2</td><td>RD</td><td>X4:2</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ff0000" border="2" sides="tb" port="w2"></td></tr><tr><td>&nbsp;</td></tr></table></td></tr></table>> fillcolor=white margin=0 shape=box style=""]
W3 [label=<<table border="0" cellspacing="0" cellpadding="0"><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td colspan="3">W3</td></tr><tr><td>2x</td><td>20 AWG (0.75 mm²)</td><td>0.2 m</td></tr></table></td></tr><tr><td>&nbsp;</td></tr><tr><td><table border="0" cellspacing="0" cellborder="0"><tr><td>X1:1</td><td>BK</td><td>X4:1</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#000000" border="2" sides="tb" port="w1"></td></tr><tr><td>X1:2</td><td>RD</td><td>X4:2</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ff0000" border="2" sides="tb" port="w2"></td></tr><tr><td>&nbsp;</td></tr></table></td></tr></table>> fillcolor=white margin=0 shape=box style=""]
}

View File

@ -119,7 +119,7 @@
<polygon fill="none" stroke="black" points="348,-84 348,-107 371,-107 371,-84 348,-84"/>
<text text-anchor="start" x="352" y="-91.8" font-family="arial" font-size="14.00">2x</text>
<polygon fill="none" stroke="black" points="371,-84 371,-107 503,-107 503,-84 371,-84"/>
<text text-anchor="start" x="375" y="-91.8" font-family="arial" font-size="14.00">0.25 mm² (24 AWG)</text>
<text text-anchor="start" x="375" y="-91.8" font-family="arial" font-size="14.00">20 AWG (0.75 mm²)</text>
<polygon fill="none" stroke="black" points="503,-84 503,-107 546,-107 546,-84 503,-84"/>
<text text-anchor="start" x="507" y="-91.8" font-family="arial" font-size="14.00">0.2 m</text>
<text text-anchor="start" x="445" y="-72.8" font-family="arial" font-size="14.00"> </text>
@ -255,4 +255,4 @@
</g>
</g>
</svg>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Molex Micro-Fit, female, 2 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">3</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X2, X3, X4</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Molex Micro-Fit, male, 2 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Cable 2 x 0.25 mm²</td><td align="right" style="border:1px solid #000000; padding: 4px">0.6</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2, W3</td></tr></table></body></html>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Molex Micro-Fit, female, 2 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">3</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X2, X3, X4</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Molex Micro-Fit, male, 2 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Cable, 2 x 0.25 mm²</td><td align="right" style="border:1px solid #000000; padding: 4px">0.4</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Cable, 2 x 20 AWG</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W3</td></tr></table></body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

@ -119,7 +119,7 @@
<polygon fill="none" stroke="black" points="348,-84 348,-107 371,-107 371,-84 348,-84"/>
<text text-anchor="start" x="352" y="-91.8" font-family="arial" font-size="14.00">2x</text>
<polygon fill="none" stroke="black" points="371,-84 371,-107 503,-107 503,-84 371,-84"/>
<text text-anchor="start" x="375" y="-91.8" font-family="arial" font-size="14.00">0.25 mm² (24 AWG)</text>
<text text-anchor="start" x="375" y="-91.8" font-family="arial" font-size="14.00">20 AWG (0.75 mm²)</text>
<polygon fill="none" stroke="black" points="503,-84 503,-107 546,-107 546,-84 503,-84"/>
<text text-anchor="start" x="507" y="-91.8" font-family="arial" font-size="14.00">0.2 m</text>
<text text-anchor="start" x="445" y="-72.8" font-family="arial" font-size="14.00"> </text>

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -22,6 +22,7 @@ cables:
<<: *wire_power # create from template
W3:
<<: *wire_power # create from template
gauge: 20 awg
connections:
-

View File

@ -1,5 +1,5 @@
Item Qty Unit Designators
Molex Micro-Fit, female, 2 pins 3 X2, X3, X4
Molex Micro-Fit, male, 2 pins 1 X1
Wire 0.25 mm² BK 0.6 m W1
Wire 0.25 mm² RD 0.6 m W1
Connector, Molex Micro-Fit, female, 2 pins 3 X2, X3, X4
Connector, Molex Micro-Fit, male, 2 pins 1 X1
Wire, 0.25 mm², BK 0.6 m W1
Wire, 0.25 mm², RD 0.6 m W1

1 Item Qty Unit Designators
2 Molex Micro-Fit, female, 2 pins Connector, Molex Micro-Fit, female, 2 pins 3 X2, X3, X4
3 Molex Micro-Fit, male, 2 pins Connector, Molex Micro-Fit, male, 2 pins 1 X1
4 Wire 0.25 mm² BK Wire, 0.25 mm², BK 0.6 m W1
5 Wire 0.25 mm² RD Wire, 0.25 mm², RD 0.6 m W1

View File

@ -225,4 +225,4 @@
</g>
</g>
</svg>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Molex Micro-Fit, female, 2 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">3</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X2, X3, X4</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Molex Micro-Fit, male, 2 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² BK</td><td align="right" style="border:1px solid #000000; padding: 4px">0.6</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² RD</td><td align="right" style="border:1px solid #000000; padding: 4px">0.6</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr></table></body></html>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Molex Micro-Fit, female, 2 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">3</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X2, X3, X4</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Molex Micro-Fit, male, 2 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², BK</td><td align="right" style="border:1px solid #000000; padding: 4px">0.6</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², RD</td><td align="right" style="border:1px solid #000000; padding: 4px">0.6</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr></table></body></html>

View File

@ -1,8 +1,8 @@
Item Qty Unit Designators
Crimp ferrule 12
Wire 0.25 mm² BN 0.2 m W1
Wire 0.25 mm² BU 0.2 m W1
Wire 0.25 mm² GN 0.2 m W1
Wire 0.25 mm² OG 0.2 m W1
Wire 0.25 mm² RD 0.2 m W1
Wire 0.25 mm² YE 0.2 m W1
Connector, Crimp ferrule 12
Wire, 0.25 mm², BN 0.2 m W1
Wire, 0.25 mm², BU 0.2 m W1
Wire, 0.25 mm², GN 0.2 m W1
Wire, 0.25 mm², OG 0.2 m W1
Wire, 0.25 mm², RD 0.2 m W1
Wire, 0.25 mm², YE 0.2 m W1

1 Item Qty Unit Designators
2 Crimp ferrule Connector, Crimp ferrule 12
3 Wire 0.25 mm² BN Wire, 0.25 mm², BN 0.2 m W1
4 Wire 0.25 mm² BU Wire, 0.25 mm², BU 0.2 m W1
5 Wire 0.25 mm² GN Wire, 0.25 mm², GN 0.2 m W1
6 Wire 0.25 mm² OG Wire, 0.25 mm², OG 0.2 m W1
7 Wire 0.25 mm² RD Wire, 0.25 mm², RD 0.2 m W1
8 Wire 0.25 mm² YE Wire, 0.25 mm², YE 0.2 m W1

View File

@ -4,7 +4,7 @@ graph {
graph [bgcolor=white fontname=arial nodesep=0.33 rankdir=LR ranksep=2]
node [fillcolor=white fontname=arial shape=record style=filled]
edge [fontname=arial style=bold]
F1 [label=<
_F1 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> Crimp ferrule </TD>
@ -14,7 +14,7 @@ graph {
> margin=0 orientation=180 shape=none style=filled]
F2 [label=<
_F2 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> Crimp ferrule </TD>
@ -24,7 +24,7 @@ graph {
> margin=0 orientation=180 shape=none style=filled]
F3 [label=<
_F3 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> Crimp ferrule </TD>
@ -34,7 +34,7 @@ graph {
> margin=0 orientation=180 shape=none style=filled]
F4 [label=<
_F4 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> Crimp ferrule </TD>
@ -44,7 +44,7 @@ graph {
> margin=0 orientation=180 shape=none style=filled]
F5 [label=<
_F5 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> Crimp ferrule </TD>
@ -54,7 +54,7 @@ graph {
> margin=0 orientation=180 shape=none style=filled]
F6 [label=<
_F6 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> Crimp ferrule </TD>
@ -64,7 +64,7 @@ graph {
> margin=0 orientation=180 shape=none style=filled]
F7 [label=<
_F7 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> </TD>
@ -74,7 +74,7 @@ graph {
> margin=0 orientation=0 shape=none style=filled]
F8 [label=<
_F8 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> </TD>
@ -84,7 +84,7 @@ graph {
> margin=0 orientation=0 shape=none style=filled]
F9 [label=<
_F9 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> </TD>
@ -94,7 +94,7 @@ graph {
> margin=0 orientation=0 shape=none style=filled]
F10 [label=<
_F10 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> </TD>
@ -104,7 +104,7 @@ graph {
> margin=0 orientation=0 shape=none style=filled]
F11 [label=<
_F11 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> </TD>
@ -114,7 +114,7 @@ graph {
> margin=0 orientation=0 shape=none style=filled]
F12 [label=<
_F12 [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> </TD>
@ -125,28 +125,28 @@ graph {
> margin=0 orientation=0 shape=none style=filled]
edge [color="#000000:#666600:#000000"]
F1:e -- W1:w1:w
_F1:e -- W1:w1:w
edge [color="#000000:#ff0000:#000000"]
F2:e -- W1:w2:w
_F2:e -- W1:w2:w
edge [color="#000000:#ff8000:#000000"]
F3:e -- W1:w3:w
_F3:e -- W1:w3:w
edge [color="#000000:#ffff00:#000000"]
F4:e -- W1:w4:w
_F4:e -- W1:w4:w
edge [color="#000000:#00ff00:#000000"]
F5:e -- W1:w5:w
_F5:e -- W1:w5:w
edge [color="#000000:#0066ff:#000000"]
F6:e -- W1:w6:w
_F6:e -- W1:w6:w
edge [color="#000000:#0066ff:#000000"]
W1:w6:e -- F7:w
W1:w6:e -- _F7:w
edge [color="#000000:#00ff00:#000000"]
W1:w5:e -- F8:w
W1:w5:e -- _F8:w
edge [color="#000000:#ffff00:#000000"]
W1:w4:e -- F9:w
W1:w4:e -- _F9:w
edge [color="#000000:#ff8000:#000000"]
W1:w3:e -- F10:w
W1:w3:e -- _F10:w
edge [color="#000000:#ff0000:#000000"]
W1:w2:e -- F11:w
W1:w2:e -- _F11:w
edge [color="#000000:#666600:#000000"]
W1:w1:e -- F12:w
W1:w1:e -- _F12:w
W1 [label=<<table border="0" cellspacing="0" cellpadding="0"><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td colspan="3">W1</td></tr><tr><td>6x</td><td>0.25 mm² (24 AWG)</td><td>0.2 m</td></tr></table></td></tr><tr><td>&nbsp;</td></tr><tr><td><table border="0" cellspacing="0" cellborder="0"><tr><td></td><td>BN</td><td></td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#666600" border="2" sides="tb" port="w1"></td></tr><tr><td></td><td>RD</td><td></td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ff0000" border="2" sides="tb" port="w2"></td></tr><tr><td></td><td>OG</td><td></td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ff8000" border="2" sides="tb" port="w3"></td></tr><tr><td></td><td>YE</td><td></td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ffff00" border="2" sides="tb" port="w4"></td></tr><tr><td></td><td>GN</td><td></td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#00ff00" border="2" sides="tb" port="w5"></td></tr><tr><td></td><td>BU</td><td></td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#0066ff" border="2" sides="tb" port="w6"></td></tr><tr><td>&nbsp;</td></tr></table></td></tr></table>> fillcolor=white margin=0 shape=box style="filled,dashed"]
}

View File

@ -8,9 +8,9 @@
viewBox="0.00 0.00 712.00 344.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 340)">
<polygon fill="white" stroke="transparent" points="-4,4 -4,-340 708,-340 708,4 -4,4"/>
<!-- F1 -->
<!-- _F1 -->
<g id="node1" class="node">
<title>F1</title>
<title>_F1</title>
<polygon fill="white" stroke="transparent" points="0,-300 109,-300 109,-336 0,-336 0,-300"/>
<text text-anchor="start" x="3.5" y="-313.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<text text-anchor="start" x="98.5" y="-313.8" font-family="arial" font-size="14.00"> &#160;</text>
@ -55,178 +55,178 @@
<polyline fill="none" stroke="black" stroke-width="2" points="450,-107 254,-107 "/>
<text text-anchor="start" x="281.5" y="-88.8" font-family="arial" font-size="14.00"> </text>
</g>
<!-- F1&#45;&#45;W1 -->
<!-- _F1&#45;&#45;W1 -->
<g id="edge1" class="edge">
<title>F1:e&#45;&#45;W1:w</title>
<title>_F1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-316C182.01,-318.14 176,-230.14 253,-228"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M109,-318C184,-318 178,-230 253,-230"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-320C186,-317.86 179.99,-229.86 253,-232"/>
</g>
<!-- F2 -->
<!-- _F2 -->
<g id="node2" class="node">
<title>F2</title>
<title>_F2</title>
<polygon fill="white" stroke="transparent" points="0,-240 109,-240 109,-276 0,-276 0,-240"/>
<text text-anchor="start" x="3.5" y="-253.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<text text-anchor="start" x="98.5" y="-253.8" font-family="arial" font-size="14.00"> &#160;</text>
<polygon fill="none" stroke="black" points="0,-247.5 0,-268.5 109,-268.5 109,-247.5 0,-247.5"/>
</g>
<!-- F2&#45;&#45;W1 -->
<!-- _F2&#45;&#45;W1 -->
<g id="edge2" class="edge">
<title>F2:e&#45;&#45;W1:w</title>
<title>_F2:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-256C175.22,-257.72 182.82,-204.72 253,-203"/>
<path fill="none" stroke="#ff0000" stroke-width="2" d="M109,-258C177.2,-258 184.8,-205 253,-205"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-260C179.18,-258.28 186.78,-205.28 253,-207"/>
</g>
<!-- F3 -->
<!-- _F3 -->
<g id="node3" class="node">
<title>F3</title>
<title>_F3</title>
<polygon fill="white" stroke="transparent" points="0,-180 109,-180 109,-216 0,-216 0,-180"/>
<text text-anchor="start" x="3.5" y="-193.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<text text-anchor="start" x="98.5" y="-193.8" font-family="arial" font-size="14.00"> &#160;</text>
<polygon fill="none" stroke="black" points="0,-187.5 0,-208.5 109,-208.5 109,-187.5 0,-187.5"/>
</g>
<!-- F3&#45;&#45;W1 -->
<!-- _F3&#45;&#45;W1 -->
<g id="edge3" class="edge">
<title>F3:e&#45;&#45;W1:w</title>
<title>_F3:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-196C171.96,-196.72 186.97,-178.72 253,-178"/>
<path fill="none" stroke="#ff8000" stroke-width="2" d="M109,-198C173.5,-198 188.5,-180 253,-180"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-200C175.03,-199.28 190.04,-181.28 253,-182"/>
</g>
<!-- F4 -->
<!-- _F4 -->
<g id="node4" class="node">
<title>F4</title>
<title>_F4</title>
<polygon fill="white" stroke="transparent" points="0,-120 109,-120 109,-156 0,-156 0,-120"/>
<text text-anchor="start" x="3.5" y="-133.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<text text-anchor="start" x="98.5" y="-133.8" font-family="arial" font-size="14.00"> &#160;</text>
<polygon fill="none" stroke="black" points="0,-127.5 0,-148.5 109,-148.5 109,-127.5 0,-127.5"/>
</g>
<!-- F4&#45;&#45;W1 -->
<!-- _F4&#45;&#45;W1 -->
<g id="edge4" class="edge">
<title>F4:e&#45;&#45;W1:w</title>
<title>_F4:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-136C174.94,-136.67 190.05,-153.67 253,-153"/>
<path fill="none" stroke="#ffff00" stroke-width="2" d="M109,-138C173.44,-138 188.56,-155 253,-155"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-140C171.95,-139.33 187.06,-156.33 253,-157"/>
</g>
<!-- F5 -->
<!-- _F5 -->
<g id="node5" class="node">
<title>F5</title>
<title>_F5</title>
<polygon fill="white" stroke="transparent" points="0,-60 109,-60 109,-96 0,-96 0,-60"/>
<text text-anchor="start" x="3.5" y="-73.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<text text-anchor="start" x="98.5" y="-73.8" font-family="arial" font-size="14.00"> &#160;</text>
<polygon fill="none" stroke="black" points="0,-67.5 0,-88.5 109,-88.5 109,-67.5 0,-67.5"/>
</g>
<!-- F5&#45;&#45;W1 -->
<!-- _F5&#45;&#45;W1 -->
<g id="edge5" class="edge">
<title>F5:e&#45;&#45;W1:w</title>
<title>_F5:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-76C179.02,-77.7 186.93,-129.7 253,-128"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M109,-78C177.05,-78 184.95,-130 253,-130"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-80C175.07,-78.3 182.98,-130.3 253,-132"/>
</g>
<!-- F6 -->
<!-- _F6 -->
<g id="node6" class="node">
<title>F6</title>
<title>_F6</title>
<polygon fill="white" stroke="transparent" points="0,0 109,0 109,-36 0,-36 0,0"/>
<text text-anchor="start" x="3.5" y="-13.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<text text-anchor="start" x="98.5" y="-13.8" font-family="arial" font-size="14.00"> &#160;</text>
<polygon fill="none" stroke="black" points="0,-7.5 0,-28.5 109,-28.5 109,-7.5 0,-7.5"/>
</g>
<!-- F6&#45;&#45;W1 -->
<!-- _F6&#45;&#45;W1 -->
<g id="edge6" class="edge">
<title>F6:e&#45;&#45;W1:w</title>
<title>_F6:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-16C185.77,-18.13 180.22,-105.13 253,-103"/>
<path fill="none" stroke="#0066ff" stroke-width="2" d="M109,-18C183.77,-18 178.23,-105 253,-105"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-20C181.78,-17.87 176.23,-104.87 253,-107"/>
</g>
<!-- F7 -->
<!-- _F7 -->
<g id="node7" class="node">
<title>F7</title>
<title>_F7</title>
<polygon fill="white" stroke="transparent" points="704,-36 595,-36 595,0 704,0 704,-36"/>
<text text-anchor="start" x="598.5" y="-13.8" font-family="arial" font-size="14.00"> &#160;</text>
<text text-anchor="start" x="610.5" y="-13.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<polygon fill="none" stroke="black" points="595,-7.5 595,-28.5 704,-28.5 704,-7.5 595,-7.5"/>
</g>
<!-- F8 -->
<!-- _F8 -->
<g id="node8" class="node">
<title>F8</title>
<title>_F8</title>
<polygon fill="white" stroke="transparent" points="704,-96 595,-96 595,-60 704,-60 704,-96"/>
<text text-anchor="start" x="598.5" y="-73.8" font-family="arial" font-size="14.00"> &#160;</text>
<text text-anchor="start" x="610.5" y="-73.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<polygon fill="none" stroke="black" points="595,-67.5 595,-88.5 704,-88.5 704,-67.5 595,-67.5"/>
</g>
<!-- F9 -->
<!-- _F9 -->
<g id="node9" class="node">
<title>F9</title>
<title>_F9</title>
<polygon fill="white" stroke="transparent" points="704,-156 595,-156 595,-120 704,-120 704,-156"/>
<text text-anchor="start" x="598.5" y="-133.8" font-family="arial" font-size="14.00"> &#160;</text>
<text text-anchor="start" x="610.5" y="-133.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<polygon fill="none" stroke="black" points="595,-127.5 595,-148.5 704,-148.5 704,-127.5 595,-127.5"/>
</g>
<!-- F10 -->
<!-- _F10 -->
<g id="node10" class="node">
<title>F10</title>
<title>_F10</title>
<polygon fill="white" stroke="transparent" points="704,-216 595,-216 595,-180 704,-180 704,-216"/>
<text text-anchor="start" x="598.5" y="-193.8" font-family="arial" font-size="14.00"> &#160;</text>
<text text-anchor="start" x="610.5" y="-193.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<polygon fill="none" stroke="black" points="595,-187.5 595,-208.5 704,-208.5 704,-187.5 595,-187.5"/>
</g>
<!-- F11 -->
<!-- _F11 -->
<g id="node11" class="node">
<title>F11</title>
<title>_F11</title>
<polygon fill="white" stroke="transparent" points="704,-276 595,-276 595,-240 704,-240 704,-276"/>
<text text-anchor="start" x="598.5" y="-253.8" font-family="arial" font-size="14.00"> &#160;</text>
<text text-anchor="start" x="610.5" y="-253.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<polygon fill="none" stroke="black" points="595,-247.5 595,-268.5 704,-268.5 704,-247.5 595,-247.5"/>
</g>
<!-- F12 -->
<!-- _F12 -->
<g id="node12" class="node">
<title>F12</title>
<title>_F12</title>
<polygon fill="white" stroke="transparent" points="704,-336 595,-336 595,-300 704,-300 704,-336"/>
<text text-anchor="start" x="598.5" y="-313.8" font-family="arial" font-size="14.00"> &#160;</text>
<text text-anchor="start" x="610.5" y="-313.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<polygon fill="none" stroke="black" points="595,-307.5 595,-328.5 704,-328.5 704,-307.5 595,-307.5"/>
</g>
<!-- W1&#45;&#45;F7 -->
<!-- W1&#45;&#45;_F7 -->
<g id="edge7" class="edge">
<title>W1:e&#45;&#45;F7:w</title>
<title>W1:e&#45;&#45;_F7:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-103C523.78,-105.13 518.23,-18.13 595,-16"/>
<path fill="none" stroke="#0066ff" stroke-width="2" d="M451,-105C525.77,-105 520.23,-18 595,-18"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-107C527.77,-104.87 522.22,-17.87 595,-20"/>
</g>
<!-- W1&#45;&#45;F8 -->
<!-- W1&#45;&#45;_F8 -->
<g id="edge8" class="edge">
<title>W1:e&#45;&#45;F8:w</title>
<title>W1:e&#45;&#45;_F8:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-128C517.07,-129.7 524.98,-77.7 595,-76"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M451,-130C519.05,-130 526.95,-78 595,-78"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-132C521.02,-130.3 528.93,-78.3 595,-80"/>
</g>
<!-- W1&#45;&#45;F9 -->
<!-- W1&#45;&#45;_F9 -->
<g id="edge9" class="edge">
<title>W1:e&#45;&#45;F9:w</title>
<title>W1:e&#45;&#45;_F9:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-153C513.95,-153.67 529.06,-136.67 595,-136"/>
<path fill="none" stroke="#ffff00" stroke-width="2" d="M451,-155C515.44,-155 530.56,-138 595,-138"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-157C516.94,-156.33 532.05,-139.33 595,-140"/>
</g>
<!-- W1&#45;&#45;F10 -->
<!-- W1&#45;&#45;_F10 -->
<g id="edge10" class="edge">
<title>W1:e&#45;&#45;F10:w</title>
<title>W1:e&#45;&#45;_F10:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-178C517.03,-178.72 532.04,-196.72 595,-196"/>
<path fill="none" stroke="#ff8000" stroke-width="2" d="M451,-180C515.5,-180 530.5,-198 595,-198"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-182C513.96,-181.28 528.97,-199.28 595,-200"/>
</g>
<!-- W1&#45;&#45;F11 -->
<!-- W1&#45;&#45;_F11 -->
<g id="edge11" class="edge">
<title>W1:e&#45;&#45;F11:w</title>
<title>W1:e&#45;&#45;_F11:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-203C521.18,-204.72 528.78,-257.72 595,-256"/>
<path fill="none" stroke="#ff0000" stroke-width="2" d="M451,-205C519.2,-205 526.8,-258 595,-258"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-207C517.22,-205.28 524.82,-258.28 595,-260"/>
</g>
<!-- W1&#45;&#45;F12 -->
<!-- W1&#45;&#45;_F12 -->
<g id="edge12" class="edge">
<title>W1:e&#45;&#45;F12:w</title>
<title>W1:e&#45;&#45;_F12:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-228C528,-230.14 521.99,-318.14 595,-316"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M451,-230C526,-230 520,-318 595,-318"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-232C524.01,-229.86 518,-317.86 595,-320"/>
</g>
</g>
</svg>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Crimp ferrule</td><td align="right" style="border:1px solid #000000; padding: 4px">12</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px"></td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² BN</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² BU</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² GN</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² OG</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² RD</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² YE</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr></table></body></html>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Crimp ferrule</td><td align="right" style="border:1px solid #000000; padding: 4px">12</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px"></td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², BN</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², BU</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², GN</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², OG</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², RD</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², YE</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr></table></body></html>

View File

@ -8,9 +8,9 @@
viewBox="0.00 0.00 712.00 344.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 340)">
<polygon fill="white" stroke="transparent" points="-4,4 -4,-340 708,-340 708,4 -4,4"/>
<!-- F1 -->
<!-- _F1 -->
<g id="node1" class="node">
<title>F1</title>
<title>_F1</title>
<polygon fill="white" stroke="transparent" points="0,-300 109,-300 109,-336 0,-336 0,-300"/>
<text text-anchor="start" x="3.5" y="-313.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<text text-anchor="start" x="98.5" y="-313.8" font-family="arial" font-size="14.00"> &#160;</text>
@ -55,174 +55,174 @@
<polyline fill="none" stroke="black" stroke-width="2" points="450,-107 254,-107 "/>
<text text-anchor="start" x="281.5" y="-88.8" font-family="arial" font-size="14.00"> </text>
</g>
<!-- F1&#45;&#45;W1 -->
<!-- _F1&#45;&#45;W1 -->
<g id="edge1" class="edge">
<title>F1:e&#45;&#45;W1:w</title>
<title>_F1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-316C182.01,-318.14 176,-230.14 253,-228"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M109,-318C184,-318 178,-230 253,-230"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-320C186,-317.86 179.99,-229.86 253,-232"/>
</g>
<!-- F2 -->
<!-- _F2 -->
<g id="node2" class="node">
<title>F2</title>
<title>_F2</title>
<polygon fill="white" stroke="transparent" points="0,-240 109,-240 109,-276 0,-276 0,-240"/>
<text text-anchor="start" x="3.5" y="-253.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<text text-anchor="start" x="98.5" y="-253.8" font-family="arial" font-size="14.00"> &#160;</text>
<polygon fill="none" stroke="black" points="0,-247.5 0,-268.5 109,-268.5 109,-247.5 0,-247.5"/>
</g>
<!-- F2&#45;&#45;W1 -->
<!-- _F2&#45;&#45;W1 -->
<g id="edge2" class="edge">
<title>F2:e&#45;&#45;W1:w</title>
<title>_F2:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-256C175.22,-257.72 182.82,-204.72 253,-203"/>
<path fill="none" stroke="#ff0000" stroke-width="2" d="M109,-258C177.2,-258 184.8,-205 253,-205"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-260C179.18,-258.28 186.78,-205.28 253,-207"/>
</g>
<!-- F3 -->
<!-- _F3 -->
<g id="node3" class="node">
<title>F3</title>
<title>_F3</title>
<polygon fill="white" stroke="transparent" points="0,-180 109,-180 109,-216 0,-216 0,-180"/>
<text text-anchor="start" x="3.5" y="-193.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<text text-anchor="start" x="98.5" y="-193.8" font-family="arial" font-size="14.00"> &#160;</text>
<polygon fill="none" stroke="black" points="0,-187.5 0,-208.5 109,-208.5 109,-187.5 0,-187.5"/>
</g>
<!-- F3&#45;&#45;W1 -->
<!-- _F3&#45;&#45;W1 -->
<g id="edge3" class="edge">
<title>F3:e&#45;&#45;W1:w</title>
<title>_F3:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-196C171.96,-196.72 186.97,-178.72 253,-178"/>
<path fill="none" stroke="#ff8000" stroke-width="2" d="M109,-198C173.5,-198 188.5,-180 253,-180"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-200C175.03,-199.28 190.04,-181.28 253,-182"/>
</g>
<!-- F4 -->
<!-- _F4 -->
<g id="node4" class="node">
<title>F4</title>
<title>_F4</title>
<polygon fill="white" stroke="transparent" points="0,-120 109,-120 109,-156 0,-156 0,-120"/>
<text text-anchor="start" x="3.5" y="-133.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<text text-anchor="start" x="98.5" y="-133.8" font-family="arial" font-size="14.00"> &#160;</text>
<polygon fill="none" stroke="black" points="0,-127.5 0,-148.5 109,-148.5 109,-127.5 0,-127.5"/>
</g>
<!-- F4&#45;&#45;W1 -->
<!-- _F4&#45;&#45;W1 -->
<g id="edge4" class="edge">
<title>F4:e&#45;&#45;W1:w</title>
<title>_F4:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-136C174.94,-136.67 190.05,-153.67 253,-153"/>
<path fill="none" stroke="#ffff00" stroke-width="2" d="M109,-138C173.44,-138 188.56,-155 253,-155"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-140C171.95,-139.33 187.06,-156.33 253,-157"/>
</g>
<!-- F5 -->
<!-- _F5 -->
<g id="node5" class="node">
<title>F5</title>
<title>_F5</title>
<polygon fill="white" stroke="transparent" points="0,-60 109,-60 109,-96 0,-96 0,-60"/>
<text text-anchor="start" x="3.5" y="-73.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<text text-anchor="start" x="98.5" y="-73.8" font-family="arial" font-size="14.00"> &#160;</text>
<polygon fill="none" stroke="black" points="0,-67.5 0,-88.5 109,-88.5 109,-67.5 0,-67.5"/>
</g>
<!-- F5&#45;&#45;W1 -->
<!-- _F5&#45;&#45;W1 -->
<g id="edge5" class="edge">
<title>F5:e&#45;&#45;W1:w</title>
<title>_F5:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-76C179.02,-77.7 186.93,-129.7 253,-128"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M109,-78C177.05,-78 184.95,-130 253,-130"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-80C175.07,-78.3 182.98,-130.3 253,-132"/>
</g>
<!-- F6 -->
<!-- _F6 -->
<g id="node6" class="node">
<title>F6</title>
<title>_F6</title>
<polygon fill="white" stroke="transparent" points="0,0 109,0 109,-36 0,-36 0,0"/>
<text text-anchor="start" x="3.5" y="-13.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<text text-anchor="start" x="98.5" y="-13.8" font-family="arial" font-size="14.00"> &#160;</text>
<polygon fill="none" stroke="black" points="0,-7.5 0,-28.5 109,-28.5 109,-7.5 0,-7.5"/>
</g>
<!-- F6&#45;&#45;W1 -->
<!-- _F6&#45;&#45;W1 -->
<g id="edge6" class="edge">
<title>F6:e&#45;&#45;W1:w</title>
<title>_F6:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-16C185.77,-18.13 180.22,-105.13 253,-103"/>
<path fill="none" stroke="#0066ff" stroke-width="2" d="M109,-18C183.77,-18 178.23,-105 253,-105"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M109,-20C181.78,-17.87 176.23,-104.87 253,-107"/>
</g>
<!-- F7 -->
<!-- _F7 -->
<g id="node7" class="node">
<title>F7</title>
<title>_F7</title>
<polygon fill="white" stroke="transparent" points="704,-36 595,-36 595,0 704,0 704,-36"/>
<text text-anchor="start" x="598.5" y="-13.8" font-family="arial" font-size="14.00"> &#160;</text>
<text text-anchor="start" x="610.5" y="-13.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<polygon fill="none" stroke="black" points="595,-7.5 595,-28.5 704,-28.5 704,-7.5 595,-7.5"/>
</g>
<!-- F8 -->
<!-- _F8 -->
<g id="node8" class="node">
<title>F8</title>
<title>_F8</title>
<polygon fill="white" stroke="transparent" points="704,-96 595,-96 595,-60 704,-60 704,-96"/>
<text text-anchor="start" x="598.5" y="-73.8" font-family="arial" font-size="14.00"> &#160;</text>
<text text-anchor="start" x="610.5" y="-73.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<polygon fill="none" stroke="black" points="595,-67.5 595,-88.5 704,-88.5 704,-67.5 595,-67.5"/>
</g>
<!-- F9 -->
<!-- _F9 -->
<g id="node9" class="node">
<title>F9</title>
<title>_F9</title>
<polygon fill="white" stroke="transparent" points="704,-156 595,-156 595,-120 704,-120 704,-156"/>
<text text-anchor="start" x="598.5" y="-133.8" font-family="arial" font-size="14.00"> &#160;</text>
<text text-anchor="start" x="610.5" y="-133.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<polygon fill="none" stroke="black" points="595,-127.5 595,-148.5 704,-148.5 704,-127.5 595,-127.5"/>
</g>
<!-- F10 -->
<!-- _F10 -->
<g id="node10" class="node">
<title>F10</title>
<title>_F10</title>
<polygon fill="white" stroke="transparent" points="704,-216 595,-216 595,-180 704,-180 704,-216"/>
<text text-anchor="start" x="598.5" y="-193.8" font-family="arial" font-size="14.00"> &#160;</text>
<text text-anchor="start" x="610.5" y="-193.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<polygon fill="none" stroke="black" points="595,-187.5 595,-208.5 704,-208.5 704,-187.5 595,-187.5"/>
</g>
<!-- F11 -->
<!-- _F11 -->
<g id="node11" class="node">
<title>F11</title>
<title>_F11</title>
<polygon fill="white" stroke="transparent" points="704,-276 595,-276 595,-240 704,-240 704,-276"/>
<text text-anchor="start" x="598.5" y="-253.8" font-family="arial" font-size="14.00"> &#160;</text>
<text text-anchor="start" x="610.5" y="-253.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<polygon fill="none" stroke="black" points="595,-247.5 595,-268.5 704,-268.5 704,-247.5 595,-247.5"/>
</g>
<!-- F12 -->
<!-- _F12 -->
<g id="node12" class="node">
<title>F12</title>
<title>_F12</title>
<polygon fill="white" stroke="transparent" points="704,-336 595,-336 595,-300 704,-300 704,-336"/>
<text text-anchor="start" x="598.5" y="-313.8" font-family="arial" font-size="14.00"> &#160;</text>
<text text-anchor="start" x="610.5" y="-313.8" font-family="arial" font-size="14.00"> Crimp ferrule &#160;</text>
<polygon fill="none" stroke="black" points="595,-307.5 595,-328.5 704,-328.5 704,-307.5 595,-307.5"/>
</g>
<!-- W1&#45;&#45;F7 -->
<!-- W1&#45;&#45;_F7 -->
<g id="edge7" class="edge">
<title>W1:e&#45;&#45;F7:w</title>
<title>W1:e&#45;&#45;_F7:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-103C523.78,-105.13 518.23,-18.13 595,-16"/>
<path fill="none" stroke="#0066ff" stroke-width="2" d="M451,-105C525.77,-105 520.23,-18 595,-18"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-107C527.77,-104.87 522.22,-17.87 595,-20"/>
</g>
<!-- W1&#45;&#45;F8 -->
<!-- W1&#45;&#45;_F8 -->
<g id="edge8" class="edge">
<title>W1:e&#45;&#45;F8:w</title>
<title>W1:e&#45;&#45;_F8:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-128C517.07,-129.7 524.98,-77.7 595,-76"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M451,-130C519.05,-130 526.95,-78 595,-78"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-132C521.02,-130.3 528.93,-78.3 595,-80"/>
</g>
<!-- W1&#45;&#45;F9 -->
<!-- W1&#45;&#45;_F9 -->
<g id="edge9" class="edge">
<title>W1:e&#45;&#45;F9:w</title>
<title>W1:e&#45;&#45;_F9:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-153C513.95,-153.67 529.06,-136.67 595,-136"/>
<path fill="none" stroke="#ffff00" stroke-width="2" d="M451,-155C515.44,-155 530.56,-138 595,-138"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-157C516.94,-156.33 532.05,-139.33 595,-140"/>
</g>
<!-- W1&#45;&#45;F10 -->
<!-- W1&#45;&#45;_F10 -->
<g id="edge10" class="edge">
<title>W1:e&#45;&#45;F10:w</title>
<title>W1:e&#45;&#45;_F10:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-178C517.03,-178.72 532.04,-196.72 595,-196"/>
<path fill="none" stroke="#ff8000" stroke-width="2" d="M451,-180C515.5,-180 530.5,-198 595,-198"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-182C513.96,-181.28 528.97,-199.28 595,-200"/>
</g>
<!-- W1&#45;&#45;F11 -->
<!-- W1&#45;&#45;_F11 -->
<g id="edge11" class="edge">
<title>W1:e&#45;&#45;F11:w</title>
<title>W1:e&#45;&#45;_F11:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-203C521.18,-204.72 528.78,-257.72 595,-256"/>
<path fill="none" stroke="#ff0000" stroke-width="2" d="M451,-205C519.2,-205 526.8,-258 595,-258"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-207C517.22,-205.28 524.82,-258.28 595,-260"/>
</g>
<!-- W1&#45;&#45;F12 -->
<!-- W1&#45;&#45;_F12 -->
<g id="edge12" class="edge">
<title>W1:e&#45;&#45;F12:w</title>
<title>W1:e&#45;&#45;_F12:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-228C528,-230.14 521.99,-318.14 595,-316"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M451,-230C526,-230 520,-318 595,-318"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M451,-232C524.01,-229.86 518,-317.86 595,-320"/>

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,6 +1,6 @@
Item Qty Unit Designators
Molex KK 254, female, 4 pins 3 X1, X2, X3
Wire 0.25 mm² PK 0.4 m W1, W2
Wire 0.25 mm² TQ 0.4 m W1, W2
Wire 0.25 mm² VT 0.4 m W1, W2
Wire 0.25 mm² YE 0.4 m W1, W2
Connector, Molex KK 254, female, 4 pins 3 X1, X2, X3
Wire, 0.25 mm², PK 0.4 m W1, W2
Wire, 0.25 mm², TQ 0.4 m W1, W2
Wire, 0.25 mm², VT 0.4 m W1, W2
Wire, 0.25 mm², YE 0.4 m W1, W2

1 Item Qty Unit Designators
2 Molex KK 254, female, 4 pins Connector, Molex KK 254, female, 4 pins 3 X1, X2, X3
3 Wire 0.25 mm² PK Wire, 0.25 mm², PK 0.4 m W1, W2
4 Wire 0.25 mm² TQ Wire, 0.25 mm², TQ 0.4 m W1, W2
5 Wire 0.25 mm² VT Wire, 0.25 mm², VT 0.4 m W1, W2
6 Wire 0.25 mm² YE Wire, 0.25 mm², YE 0.4 m W1, W2

View File

@ -292,4 +292,4 @@
</g>
</g>
</svg>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Molex KK 254, female, 4 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">3</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1, X2, X3</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² PK</td><td align="right" style="border:1px solid #000000; padding: 4px">0.4</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² TQ</td><td align="right" style="border:1px solid #000000; padding: 4px">0.4</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² VT</td><td align="right" style="border:1px solid #000000; padding: 4px">0.4</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² YE</td><td align="right" style="border:1px solid #000000; padding: 4px">0.4</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr></table></body></html>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Molex KK 254, female, 4 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">3</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1, X2, X3</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², PK</td><td align="right" style="border:1px solid #000000; padding: 4px">0.4</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², TQ</td><td align="right" style="border:1px solid #000000; padding: 4px">0.4</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², VT</td><td align="right" style="border:1px solid #000000; padding: 4px">0.4</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², YE</td><td align="right" style="border:1px solid #000000; padding: 4px">0.4</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2</td></tr></table></body></html>

View File

@ -1,6 +1,6 @@
Item Qty Unit Designators
Molex KK 254, female, 4 pins 6 X1, X2, X3, X4, X5, X6
Wire 0.25 mm² PK 1.0 m W1, W2, W3, W4, W5
Wire 0.25 mm² TQ 1.0 m W1, W2, W3, W4, W5
Wire 0.25 mm² VT 1.0 m W1, W2, W3, W4, W5
Wire 0.25 mm² YE 1.0 m W1, W2, W3, W4, W5
Connector, Molex KK 254, female, 4 pins 6 X1, X2, X3, X4, X5, X6
Wire, 0.25 mm², PK 1.0 m W1, W2, W3, W4, W5
Wire, 0.25 mm², TQ 1.0 m W1, W2, W3, W4, W5
Wire, 0.25 mm², VT 1.0 m W1, W2, W3, W4, W5
Wire, 0.25 mm², YE 1.0 m W1, W2, W3, W4, W5

1 Item Qty Unit Designators
2 Molex KK 254, female, 4 pins Connector, Molex KK 254, female, 4 pins 6 X1, X2, X3, X4, X5, X6
3 Wire 0.25 mm² PK Wire, 0.25 mm², PK 1.0 m W1, W2, W3, W4, W5
4 Wire 0.25 mm² TQ Wire, 0.25 mm², TQ 1.0 m W1, W2, W3, W4, W5
5 Wire 0.25 mm² VT Wire, 0.25 mm², VT 1.0 m W1, W2, W3, W4, W5
6 Wire 0.25 mm² YE Wire, 0.25 mm², YE 1.0 m W1, W2, W3, W4, W5

View File

@ -653,4 +653,4 @@
</g>
</g>
</svg>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Molex KK 254, female, 4 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">6</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1, X2, X3, X4, X5, X6</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² PK</td><td align="right" style="border:1px solid #000000; padding: 4px">1.0</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2, W3, W4, W5</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² TQ</td><td align="right" style="border:1px solid #000000; padding: 4px">1.0</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2, W3, W4, W5</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² VT</td><td align="right" style="border:1px solid #000000; padding: 4px">1.0</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2, W3, W4, W5</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire 0.25 mm² YE</td><td align="right" style="border:1px solid #000000; padding: 4px">1.0</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2, W3, W4, W5</td></tr></table></body></html>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Molex KK 254, female, 4 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">6</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1, X2, X3, X4, X5, X6</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², PK</td><td align="right" style="border:1px solid #000000; padding: 4px">1.0</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2, W3, W4, W5</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², TQ</td><td align="right" style="border:1px solid #000000; padding: 4px">1.0</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2, W3, W4, W5</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², VT</td><td align="right" style="border:1px solid #000000; padding: 4px">1.0</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2, W3, W4, W5</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Wire, 0.25 mm², YE</td><td align="right" style="border:1px solid #000000; padding: 4px">1.0</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1, W2, W3, W4, W5</td></tr></table></body></html>

4
examples/ex07.bom.tsv Normal file
View File

@ -0,0 +1,4 @@
Item Qty Unit Designators
Connector, D-Sub, female, 9 pins 1 X2
Connector, TE 776164-1, female, 35 pins 1 X1
Cable, 2 x 20 AWG 1 m C1
1 Item Qty Unit Designators
2 Connector, D-Sub, female, 9 pins 1 X2
3 Connector, TE 776164-1, female, 35 pins 1 X1
4 Cable, 2 x 20 AWG 1 m C1

16
examples/ex07.gv Normal file
View File

@ -0,0 +1,16 @@
graph {
// Graph generated by WireViz
// https://github.com/formatc1702/WireViz
graph [bgcolor=white fontname=arial nodesep=0.33 rankdir=LR ranksep=2]
node [fillcolor=white fontname=arial shape=record style=filled]
edge [fontname=arial style=bold]
X1 [label="X1|{TE 776164-1|female|35-pin}|{{<p5r>5|<p6r>6}}|Unconnected pins are not shown"]
X2 [label="X2|{D-Sub|female|9-pin}|{{<p2l>2|<p7l>7}}|Unconnected pins are not shown"]
edge [color="#000000:#ffff00:#000000"]
X1:p5r:e -- C1:w1:w
C1:w1:e -- X2:p7l:w
edge [color="#000000:#00ff00:#000000"]
X1:p6r:e -- C1:w2:w
C1:w2:e -- X2:p2l:w
C1 [label=<<table border="0" cellspacing="0" cellpadding="0"><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td colspan="3">C1</td></tr><tr><td>2x</td><td>20 AWG</td><td>1 m</td></tr></table></td></tr><tr><td>&nbsp;</td></tr><tr><td><table border="0" cellspacing="0" cellborder="0"><tr><td>X1:5</td><td>YE</td><td>X2:7</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ffff00" border="2" sides="tb" port="w1"></td></tr><tr><td>X1:6</td><td>GN</td><td>X2:2</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#00ff00" border="2" sides="tb" port="w2"></td></tr><tr><td>&nbsp;</td></tr></table></td></tr></table>> fillcolor=white margin=0 shape=box style=""]
}

104
examples/ex07.html Normal file
View File

@ -0,0 +1,104 @@
<html><body style="font-family:Arial"><h1>Diagram</h1><?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.44.0 (20200408.0750)
-->
<!-- Pages: 1 -->
<svg width="837pt" height="150pt"
viewBox="0.00 0.00 837.00 150.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 146)">
<polygon fill="white" stroke="transparent" points="-4,4 -4,-146 833,-146 833,4 -4,4"/>
<!-- X1 -->
<g id="node1" class="node">
<title>X1</title>
<polygon fill="white" stroke="black" points="0,-0.5 0,-115.5 213,-115.5 213,-0.5 0,-0.5"/>
<text text-anchor="middle" x="106.5" y="-100.3" font-family="arial" font-size="14.00">X1</text>
<polyline fill="none" stroke="black" points="0,-92.5 213,-92.5 "/>
<text text-anchor="middle" x="48.5" y="-77.3" font-family="arial" font-size="14.00">TE 776164&#45;1</text>
<polyline fill="none" stroke="black" points="97,-69.5 97,-92.5 "/>
<text text-anchor="middle" x="127" y="-77.3" font-family="arial" font-size="14.00">female</text>
<polyline fill="none" stroke="black" points="157,-69.5 157,-92.5 "/>
<text text-anchor="middle" x="185" y="-77.3" font-family="arial" font-size="14.00">35&#45;pin</text>
<polyline fill="none" stroke="black" points="0,-69.5 213,-69.5 "/>
<text text-anchor="middle" x="106.5" y="-54.3" font-family="arial" font-size="14.00">5</text>
<polyline fill="none" stroke="black" points="0,-46.5 213,-46.5 "/>
<text text-anchor="middle" x="106.5" y="-31.3" font-family="arial" font-size="14.00">6</text>
<polyline fill="none" stroke="black" points="0,-23.5 213,-23.5 "/>
<text text-anchor="middle" x="106.5" y="-8.3" font-family="arial" font-size="14.00">Unconnected pins are not shown</text>
</g>
<!-- C1 -->
<g id="node3" class="node">
<title>C1</title>
<polygon fill="none" stroke="black" points="472,-142 357,-142 357,-12 472,-12 472,-142"/>
<polygon fill="none" stroke="black" points="357.5,-119 357.5,-142 472.5,-142 472.5,-119 357.5,-119"/>
<text text-anchor="start" x="406" y="-126.8" font-family="arial" font-size="14.00">C1</text>
<polygon fill="none" stroke="black" points="357.5,-96 357.5,-119 380.5,-119 380.5,-96 357.5,-96"/>
<text text-anchor="start" x="361.5" y="-103.8" font-family="arial" font-size="14.00">2x</text>
<polygon fill="none" stroke="black" points="380.5,-96 380.5,-119 440.5,-119 440.5,-96 380.5,-96"/>
<text text-anchor="start" x="384.5" y="-103.8" font-family="arial" font-size="14.00">20 AWG</text>
<polygon fill="none" stroke="black" points="440.5,-96 440.5,-119 472.5,-119 472.5,-96 440.5,-96"/>
<text text-anchor="start" x="444.5" y="-103.8" font-family="arial" font-size="14.00">1 m</text>
<text text-anchor="start" x="413" y="-84.8" font-family="arial" font-size="14.00"> </text>
<text text-anchor="start" x="364" y="-67.8" font-family="arial" font-size="14.00">X1:5</text>
<text text-anchor="start" x="406" y="-67.8" font-family="arial" font-size="14.00">YE</text>
<text text-anchor="start" x="438.5" y="-67.8" font-family="arial" font-size="14.00">X2:7</text>
<polygon fill="#ffff00" stroke="transparent" points="357.5,-56 357.5,-62 472.5,-62 472.5,-56 357.5,-56"/>
<polyline fill="none" stroke="black" stroke-width="2" points="358.5,-57 471.5,-57 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="471.5,-61 358.5,-61 "/>
<text text-anchor="start" x="364" y="-42.8" font-family="arial" font-size="14.00">X1:6</text>
<text text-anchor="start" x="404.5" y="-42.8" font-family="arial" font-size="14.00">GN</text>
<text text-anchor="start" x="438.5" y="-42.8" font-family="arial" font-size="14.00">X2:2</text>
<polygon fill="#00ff00" stroke="transparent" stroke-width="2" points="357.5,-31 357.5,-37 472.5,-37 472.5,-31 357.5,-31"/>
<polyline fill="none" stroke="black" stroke-width="2" points="358.5,-32 471.5,-32 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="471.5,-36 358.5,-36 "/>
<text text-anchor="start" x="376" y="-17.8" font-family="arial" font-size="14.00"> </text>
</g>
<!-- X1&#45;&#45;C1 -->
<g id="edge1" class="edge">
<title>X1:e&#45;&#45;C1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M213,-56C277.13,-56 293.12,-57 357,-57"/>
<path fill="none" stroke="#ffff00" stroke-width="2" d="M213,-58C277,-58 293,-59 357,-59"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M213,-60C276.88,-60 292.87,-61 357,-61"/>
</g>
<!-- X1&#45;&#45;C1 -->
<g id="edge3" class="edge">
<title>X1:e&#45;&#45;C1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M213,-33C276.88,-33 292.87,-32 357,-32"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M213,-35C277,-35 293,-34 357,-34"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M213,-37C277.13,-37 293.12,-36 357,-36"/>
</g>
<!-- X2 -->
<g id="node2" class="node">
<title>X2</title>
<polygon fill="white" stroke="black" points="616,-0.5 616,-115.5 829,-115.5 829,-0.5 616,-0.5"/>
<text text-anchor="middle" x="722.5" y="-100.3" font-family="arial" font-size="14.00">X2</text>
<polyline fill="none" stroke="black" points="616,-92.5 829,-92.5 "/>
<text text-anchor="middle" x="652" y="-77.3" font-family="arial" font-size="14.00">D&#45;Sub</text>
<polyline fill="none" stroke="black" points="688,-69.5 688,-92.5 "/>
<text text-anchor="middle" x="726" y="-77.3" font-family="arial" font-size="14.00">female</text>
<polyline fill="none" stroke="black" points="764,-69.5 764,-92.5 "/>
<text text-anchor="middle" x="796.5" y="-77.3" font-family="arial" font-size="14.00">9&#45;pin</text>
<polyline fill="none" stroke="black" points="616,-69.5 829,-69.5 "/>
<text text-anchor="middle" x="722.5" y="-54.3" font-family="arial" font-size="14.00">2</text>
<polyline fill="none" stroke="black" points="616,-46.5 829,-46.5 "/>
<text text-anchor="middle" x="722.5" y="-31.3" font-family="arial" font-size="14.00">7</text>
<polyline fill="none" stroke="black" points="616,-23.5 829,-23.5 "/>
<text text-anchor="middle" x="722.5" y="-8.3" font-family="arial" font-size="14.00">Unconnected pins are not shown</text>
</g>
<!-- C1&#45;&#45;X2 -->
<g id="edge2" class="edge">
<title>C1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M472,-57C535.16,-57.98 549.4,-33.98 616,-33"/>
<path fill="none" stroke="#ffff00" stroke-width="2" d="M472,-59C536.88,-59 551.12,-35 616,-35"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M472,-61C538.6,-60.02 552.84,-36.02 616,-37"/>
</g>
<!-- C1&#45;&#45;X2 -->
<g id="edge4" class="edge">
<title>C1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M472,-32C538.6,-32.98 552.84,-56.98 616,-56"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M472,-34C536.88,-34 551.12,-58 616,-58"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M472,-36C535.16,-35.02 549.4,-59.02 616,-60"/>
</g>
</g>
</svg>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, D-Sub, female, 9 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, TE 776164-1, female, 35 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Cable, 2 x 20 AWG</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">C1</td></tr></table></body></html>

BIN
examples/ex07.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

103
examples/ex07.svg Normal file
View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.44.0 (20200408.0750)
-->
<!-- Pages: 1 -->
<svg width="837pt" height="150pt"
viewBox="0.00 0.00 837.00 150.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 146)">
<polygon fill="white" stroke="transparent" points="-4,4 -4,-146 833,-146 833,4 -4,4"/>
<!-- X1 -->
<g id="node1" class="node">
<title>X1</title>
<polygon fill="white" stroke="black" points="0,-0.5 0,-115.5 213,-115.5 213,-0.5 0,-0.5"/>
<text text-anchor="middle" x="106.5" y="-100.3" font-family="arial" font-size="14.00">X1</text>
<polyline fill="none" stroke="black" points="0,-92.5 213,-92.5 "/>
<text text-anchor="middle" x="48.5" y="-77.3" font-family="arial" font-size="14.00">TE 776164&#45;1</text>
<polyline fill="none" stroke="black" points="97,-69.5 97,-92.5 "/>
<text text-anchor="middle" x="127" y="-77.3" font-family="arial" font-size="14.00">female</text>
<polyline fill="none" stroke="black" points="157,-69.5 157,-92.5 "/>
<text text-anchor="middle" x="185" y="-77.3" font-family="arial" font-size="14.00">35&#45;pin</text>
<polyline fill="none" stroke="black" points="0,-69.5 213,-69.5 "/>
<text text-anchor="middle" x="106.5" y="-54.3" font-family="arial" font-size="14.00">5</text>
<polyline fill="none" stroke="black" points="0,-46.5 213,-46.5 "/>
<text text-anchor="middle" x="106.5" y="-31.3" font-family="arial" font-size="14.00">6</text>
<polyline fill="none" stroke="black" points="0,-23.5 213,-23.5 "/>
<text text-anchor="middle" x="106.5" y="-8.3" font-family="arial" font-size="14.00">Unconnected pins are not shown</text>
</g>
<!-- C1 -->
<g id="node3" class="node">
<title>C1</title>
<polygon fill="none" stroke="black" points="472,-142 357,-142 357,-12 472,-12 472,-142"/>
<polygon fill="none" stroke="black" points="357.5,-119 357.5,-142 472.5,-142 472.5,-119 357.5,-119"/>
<text text-anchor="start" x="406" y="-126.8" font-family="arial" font-size="14.00">C1</text>
<polygon fill="none" stroke="black" points="357.5,-96 357.5,-119 380.5,-119 380.5,-96 357.5,-96"/>
<text text-anchor="start" x="361.5" y="-103.8" font-family="arial" font-size="14.00">2x</text>
<polygon fill="none" stroke="black" points="380.5,-96 380.5,-119 440.5,-119 440.5,-96 380.5,-96"/>
<text text-anchor="start" x="384.5" y="-103.8" font-family="arial" font-size="14.00">20 AWG</text>
<polygon fill="none" stroke="black" points="440.5,-96 440.5,-119 472.5,-119 472.5,-96 440.5,-96"/>
<text text-anchor="start" x="444.5" y="-103.8" font-family="arial" font-size="14.00">1 m</text>
<text text-anchor="start" x="413" y="-84.8" font-family="arial" font-size="14.00"> </text>
<text text-anchor="start" x="364" y="-67.8" font-family="arial" font-size="14.00">X1:5</text>
<text text-anchor="start" x="406" y="-67.8" font-family="arial" font-size="14.00">YE</text>
<text text-anchor="start" x="438.5" y="-67.8" font-family="arial" font-size="14.00">X2:7</text>
<polygon fill="#ffff00" stroke="transparent" points="357.5,-56 357.5,-62 472.5,-62 472.5,-56 357.5,-56"/>
<polyline fill="none" stroke="black" stroke-width="2" points="358.5,-57 471.5,-57 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="471.5,-61 358.5,-61 "/>
<text text-anchor="start" x="364" y="-42.8" font-family="arial" font-size="14.00">X1:6</text>
<text text-anchor="start" x="404.5" y="-42.8" font-family="arial" font-size="14.00">GN</text>
<text text-anchor="start" x="438.5" y="-42.8" font-family="arial" font-size="14.00">X2:2</text>
<polygon fill="#00ff00" stroke="transparent" stroke-width="2" points="357.5,-31 357.5,-37 472.5,-37 472.5,-31 357.5,-31"/>
<polyline fill="none" stroke="black" stroke-width="2" points="358.5,-32 471.5,-32 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="471.5,-36 358.5,-36 "/>
<text text-anchor="start" x="376" y="-17.8" font-family="arial" font-size="14.00"> </text>
</g>
<!-- X1&#45;&#45;C1 -->
<g id="edge1" class="edge">
<title>X1:e&#45;&#45;C1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M213,-56C277.13,-56 293.12,-57 357,-57"/>
<path fill="none" stroke="#ffff00" stroke-width="2" d="M213,-58C277,-58 293,-59 357,-59"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M213,-60C276.88,-60 292.87,-61 357,-61"/>
</g>
<!-- X1&#45;&#45;C1 -->
<g id="edge3" class="edge">
<title>X1:e&#45;&#45;C1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M213,-33C276.88,-33 292.87,-32 357,-32"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M213,-35C277,-35 293,-34 357,-34"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M213,-37C277.13,-37 293.12,-36 357,-36"/>
</g>
<!-- X2 -->
<g id="node2" class="node">
<title>X2</title>
<polygon fill="white" stroke="black" points="616,-0.5 616,-115.5 829,-115.5 829,-0.5 616,-0.5"/>
<text text-anchor="middle" x="722.5" y="-100.3" font-family="arial" font-size="14.00">X2</text>
<polyline fill="none" stroke="black" points="616,-92.5 829,-92.5 "/>
<text text-anchor="middle" x="652" y="-77.3" font-family="arial" font-size="14.00">D&#45;Sub</text>
<polyline fill="none" stroke="black" points="688,-69.5 688,-92.5 "/>
<text text-anchor="middle" x="726" y="-77.3" font-family="arial" font-size="14.00">female</text>
<polyline fill="none" stroke="black" points="764,-69.5 764,-92.5 "/>
<text text-anchor="middle" x="796.5" y="-77.3" font-family="arial" font-size="14.00">9&#45;pin</text>
<polyline fill="none" stroke="black" points="616,-69.5 829,-69.5 "/>
<text text-anchor="middle" x="722.5" y="-54.3" font-family="arial" font-size="14.00">2</text>
<polyline fill="none" stroke="black" points="616,-46.5 829,-46.5 "/>
<text text-anchor="middle" x="722.5" y="-31.3" font-family="arial" font-size="14.00">7</text>
<polyline fill="none" stroke="black" points="616,-23.5 829,-23.5 "/>
<text text-anchor="middle" x="722.5" y="-8.3" font-family="arial" font-size="14.00">Unconnected pins are not shown</text>
</g>
<!-- C1&#45;&#45;X2 -->
<g id="edge2" class="edge">
<title>C1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M472,-57C535.16,-57.98 549.4,-33.98 616,-33"/>
<path fill="none" stroke="#ffff00" stroke-width="2" d="M472,-59C536.88,-59 551.12,-35 616,-35"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M472,-61C538.6,-60.02 552.84,-36.02 616,-37"/>
</g>
<!-- C1&#45;&#45;X2 -->
<g id="edge4" class="edge">
<title>C1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M472,-32C538.6,-32.98 552.84,-56.98 616,-56"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M472,-34C536.88,-34 551.12,-58 616,-58"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M472,-36C535.16,-35.02 549.4,-59.02 616,-60"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.7 KiB

29
examples/ex07.yml Normal file
View File

@ -0,0 +1,29 @@
# contributed by @elliotmr
connectors:
X1:
type: TE 776164-1
subtype: female
hide_disconnected_pins: True
pincount: 35
notes: Unconnected pins are not shown
X2:
type: D-Sub
subtype: female
pincount: 9
hide_disconnected_pins: True
notes: Unconnected pins are not shown
cables:
C1:
wirecount: 2
gauge: 20 AWG
colors: [YE, GN]
length: 1
connections:
-
- X1: [5,6]
- C1: [1,2]
- X2: [7,2]

3
examples/ex08.bom.tsv Normal file
View File

@ -0,0 +1,3 @@
Item Qty Unit Designators
Connector, Phone Connector, male 3.5, 3 pins 1 Key
Cable, 3 x 24 AWG shielded 0.2 m W1
1 Item Qty Unit Designators
2 Connector, Phone Connector, male 3.5, 3 pins 1 Key
3 Cable, 3 x 24 AWG shielded 0.2 m W1

17
examples/ex08.gv Normal file
View File

@ -0,0 +1,17 @@
graph {
// Graph generated by WireViz
// https://github.com/formatc1702/WireViz
graph [bgcolor=white fontname=arial nodesep=0.33 rankdir=LR ranksep=2]
node [fillcolor=white fontname=arial shape=record style=filled]
edge [fontname=arial style=bold]
Key [label="Key|{Phone Connector|male 3.5}|{{Dot|Dash|Ground}|{<pTr>T|<pRr>R|<pSr>S}}"]
edge [color="#000000:#ffffff:#000000"]
Key:pSr:e -- W1:w1:w
edge [color="#000000:#666600:#000000"]
Key:pRr:e -- W1:w2:w
edge [color="#000000:#00ff00:#000000"]
Key:pTr:e -- W1:w3:w
edge [color="#000000"]
Key:pSr:e -- W1:ws:w
W1 [label=<<table border="0" cellspacing="0" cellpadding="0"><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td colspan="4">W1</td></tr><tr><td>3x</td><td>24 AWG</td><td>+ S</td><td>0.2 m</td></tr></table></td></tr><tr><td>&nbsp;</td></tr><tr><td><table border="0" cellspacing="0" cellborder="0"><tr><td>Key:S</td><td>WH</td><td><!-- 1_out --></td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ffffff" border="2" sides="tb" port="w1"></td></tr><tr><td>Key:R</td><td>BN</td><td><!-- 2_out --></td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#666600" border="2" sides="tb" port="w2"></td></tr><tr><td>Key:T</td><td>GN</td><td><!-- 3_out --></td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#00ff00" border="2" sides="tb" port="w3"></td></tr><tr><td>&nbsp;</td></tr><tr><td>Key:S</td><td>Shield</td><td><!-- s_out --></td></tr><tr><td colspan="3" cellpadding="0" height="6" border="2" sides="b" port="ws"></td></tr><tr><td>&nbsp;</td></tr></table></td></tr></table>> fillcolor=white margin=0 shape=box style=""]
}

97
examples/ex08.html Normal file
View File

@ -0,0 +1,97 @@
<html><body style="font-family:Arial"><h1>Diagram</h1><?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.44.0 (20200408.0750)
-->
<!-- Pages: 1 -->
<svg width="499pt" height="207pt"
viewBox="0.00 0.00 499.00 207.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 203)">
<polygon fill="white" stroke="transparent" points="-4,4 -4,-203 495,-203 495,4 -4,4"/>
<!-- Key -->
<g id="node1" class="node">
<title>Key</title>
<polygon fill="white" stroke="black" points="0,-33 0,-148 191,-148 191,-33 0,-33"/>
<text text-anchor="middle" x="95.5" y="-132.8" font-family="arial" font-size="14.00">Key</text>
<polyline fill="none" stroke="black" points="0,-125 191,-125 "/>
<text text-anchor="middle" x="61" y="-109.8" font-family="arial" font-size="14.00">Phone Connector</text>
<polyline fill="none" stroke="black" points="122,-102 122,-125 "/>
<text text-anchor="middle" x="156.5" y="-109.8" font-family="arial" font-size="14.00">male 3.5</text>
<polyline fill="none" stroke="black" points="0,-102 191,-102 "/>
<text text-anchor="middle" x="56.5" y="-86.8" font-family="arial" font-size="14.00">Dot</text>
<polyline fill="none" stroke="black" points="0,-79 113,-79 "/>
<text text-anchor="middle" x="56.5" y="-63.8" font-family="arial" font-size="14.00">Dash</text>
<polyline fill="none" stroke="black" points="0,-56 113,-56 "/>
<text text-anchor="middle" x="56.5" y="-40.8" font-family="arial" font-size="14.00">Ground</text>
<polyline fill="none" stroke="black" points="113,-33 113,-102 "/>
<text text-anchor="middle" x="152" y="-86.8" font-family="arial" font-size="14.00">T</text>
<polyline fill="none" stroke="black" points="113,-79 191,-79 "/>
<text text-anchor="middle" x="152" y="-63.8" font-family="arial" font-size="14.00">R</text>
<polyline fill="none" stroke="black" points="113,-56 191,-56 "/>
<text text-anchor="middle" x="152" y="-40.8" font-family="arial" font-size="14.00">S</text>
</g>
<!-- W1 -->
<g id="node2" class="node">
<title>W1</title>
<polygon fill="none" stroke="black" points="491,-199 335,-199 335,0 491,0 491,-199"/>
<polygon fill="none" stroke="black" points="335,-175.5 335,-198.5 491,-198.5 491,-175.5 335,-175.5"/>
<text text-anchor="start" x="402" y="-183.3" font-family="arial" font-size="14.00">W1</text>
<polygon fill="none" stroke="black" points="335,-152.5 335,-175.5 358,-175.5 358,-152.5 335,-152.5"/>
<text text-anchor="start" x="339" y="-160.3" font-family="arial" font-size="14.00">3x</text>
<polygon fill="none" stroke="black" points="358,-152.5 358,-175.5 418,-175.5 418,-152.5 358,-152.5"/>
<text text-anchor="start" x="362" y="-160.3" font-family="arial" font-size="14.00">24 AWG</text>
<polygon fill="none" stroke="black" points="418,-152.5 418,-175.5 448,-175.5 448,-152.5 418,-152.5"/>
<text text-anchor="start" x="422" y="-160.3" font-family="arial" font-size="14.00">+ S</text>
<polygon fill="none" stroke="black" points="448,-152.5 448,-175.5 491,-175.5 491,-152.5 448,-152.5"/>
<text text-anchor="start" x="452" y="-160.3" font-family="arial" font-size="14.00">0.2 m</text>
<text text-anchor="start" x="411" y="-141.3" font-family="arial" font-size="14.00"> </text>
<text text-anchor="start" x="348.5" y="-124.3" font-family="arial" font-size="14.00">Key:S</text>
<text text-anchor="start" x="419.5" y="-124.3" font-family="arial" font-size="14.00">WH</text>
<polygon fill="#ffffff" stroke="transparent" points="335,-112.5 335,-118.5 491,-118.5 491,-112.5 335,-112.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="336,-113.5 490,-113.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="490,-117.5 336,-117.5 "/>
<text text-anchor="start" x="348.5" y="-99.3" font-family="arial" font-size="14.00">Key:R</text>
<text text-anchor="start" x="422" y="-99.3" font-family="arial" font-size="14.00">BN</text>
<polygon fill="#666600" stroke="transparent" stroke-width="2" points="335,-87.5 335,-93.5 491,-93.5 491,-87.5 335,-87.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="336,-88.5 490,-88.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="490,-92.5 336,-92.5 "/>
<text text-anchor="start" x="349" y="-74.3" font-family="arial" font-size="14.00">Key:T</text>
<text text-anchor="start" x="420.5" y="-74.3" font-family="arial" font-size="14.00">GN</text>
<polygon fill="#00ff00" stroke="transparent" stroke-width="2" points="335,-62.5 335,-68.5 491,-68.5 491,-62.5 335,-62.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="336,-63.5 490,-63.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="490,-67.5 336,-67.5 "/>
<text text-anchor="start" x="365" y="-49.3" font-family="arial" font-size="14.00"> </text>
<text text-anchor="start" x="348.5" y="-30.3" font-family="arial" font-size="14.00">Key:S</text>
<text text-anchor="start" x="412.5" y="-30.3" font-family="arial" font-size="14.00">Shield</text>
<polyline fill="none" stroke="black" stroke-width="2" points="336,-19.5 490,-19.5 "/>
<text text-anchor="start" x="365" y="-5.3" font-family="arial" font-size="14.00"> </text>
</g>
<!-- Key&#45;&#45;W1 -->
<g id="edge1" class="edge">
<title>Key:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-42.5C264.36,-44.46 265.64,-115.46 335,-113.5"/>
<path fill="none" stroke="#ffffff" stroke-width="2" d="M191,-44.5C262.36,-44.5 263.64,-115.5 335,-115.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-46.5C260.36,-44.54 261.64,-115.54 335,-117.5"/>
</g>
<!-- Key&#45;&#45;W1 -->
<g id="edge2" class="edge">
<title>Key:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-65.5C257.51,-66.44 271.88,-89.44 335,-88.5"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M191,-67.5C255.81,-67.5 270.19,-90.5 335,-90.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-69.5C254.12,-68.56 268.49,-91.56 335,-92.5"/>
</g>
<!-- Key&#45;&#45;W1 -->
<g id="edge3" class="edge">
<title>Key:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-88.5C254.21,-89.52 268.3,-64.52 335,-63.5"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M191,-90.5C255.96,-90.5 270.04,-65.5 335,-65.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-92.5C257.7,-91.48 271.79,-66.48 335,-67.5"/>
</g>
<!-- Key&#45;&#45;W1 -->
<g id="edge4" class="edge">
<title>Key:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-44.5C255.81,-44.5 270.19,-21.5 335,-21.5"/>
</g>
</g>
</svg>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, Phone Connector, male 3.5, 3 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">Key</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Cable, 3 x 24 AWG shielded</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr></table></body></html>

BIN
examples/ex08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

96
examples/ex08.svg Normal file
View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.44.0 (20200408.0750)
-->
<!-- Pages: 1 -->
<svg width="499pt" height="207pt"
viewBox="0.00 0.00 499.00 207.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 203)">
<polygon fill="white" stroke="transparent" points="-4,4 -4,-203 495,-203 495,4 -4,4"/>
<!-- Key -->
<g id="node1" class="node">
<title>Key</title>
<polygon fill="white" stroke="black" points="0,-33 0,-148 191,-148 191,-33 0,-33"/>
<text text-anchor="middle" x="95.5" y="-132.8" font-family="arial" font-size="14.00">Key</text>
<polyline fill="none" stroke="black" points="0,-125 191,-125 "/>
<text text-anchor="middle" x="61" y="-109.8" font-family="arial" font-size="14.00">Phone Connector</text>
<polyline fill="none" stroke="black" points="122,-102 122,-125 "/>
<text text-anchor="middle" x="156.5" y="-109.8" font-family="arial" font-size="14.00">male 3.5</text>
<polyline fill="none" stroke="black" points="0,-102 191,-102 "/>
<text text-anchor="middle" x="56.5" y="-86.8" font-family="arial" font-size="14.00">Dot</text>
<polyline fill="none" stroke="black" points="0,-79 113,-79 "/>
<text text-anchor="middle" x="56.5" y="-63.8" font-family="arial" font-size="14.00">Dash</text>
<polyline fill="none" stroke="black" points="0,-56 113,-56 "/>
<text text-anchor="middle" x="56.5" y="-40.8" font-family="arial" font-size="14.00">Ground</text>
<polyline fill="none" stroke="black" points="113,-33 113,-102 "/>
<text text-anchor="middle" x="152" y="-86.8" font-family="arial" font-size="14.00">T</text>
<polyline fill="none" stroke="black" points="113,-79 191,-79 "/>
<text text-anchor="middle" x="152" y="-63.8" font-family="arial" font-size="14.00">R</text>
<polyline fill="none" stroke="black" points="113,-56 191,-56 "/>
<text text-anchor="middle" x="152" y="-40.8" font-family="arial" font-size="14.00">S</text>
</g>
<!-- W1 -->
<g id="node2" class="node">
<title>W1</title>
<polygon fill="none" stroke="black" points="491,-199 335,-199 335,0 491,0 491,-199"/>
<polygon fill="none" stroke="black" points="335,-175.5 335,-198.5 491,-198.5 491,-175.5 335,-175.5"/>
<text text-anchor="start" x="402" y="-183.3" font-family="arial" font-size="14.00">W1</text>
<polygon fill="none" stroke="black" points="335,-152.5 335,-175.5 358,-175.5 358,-152.5 335,-152.5"/>
<text text-anchor="start" x="339" y="-160.3" font-family="arial" font-size="14.00">3x</text>
<polygon fill="none" stroke="black" points="358,-152.5 358,-175.5 418,-175.5 418,-152.5 358,-152.5"/>
<text text-anchor="start" x="362" y="-160.3" font-family="arial" font-size="14.00">24 AWG</text>
<polygon fill="none" stroke="black" points="418,-152.5 418,-175.5 448,-175.5 448,-152.5 418,-152.5"/>
<text text-anchor="start" x="422" y="-160.3" font-family="arial" font-size="14.00">+ S</text>
<polygon fill="none" stroke="black" points="448,-152.5 448,-175.5 491,-175.5 491,-152.5 448,-152.5"/>
<text text-anchor="start" x="452" y="-160.3" font-family="arial" font-size="14.00">0.2 m</text>
<text text-anchor="start" x="411" y="-141.3" font-family="arial" font-size="14.00"> </text>
<text text-anchor="start" x="348.5" y="-124.3" font-family="arial" font-size="14.00">Key:S</text>
<text text-anchor="start" x="419.5" y="-124.3" font-family="arial" font-size="14.00">WH</text>
<polygon fill="#ffffff" stroke="transparent" points="335,-112.5 335,-118.5 491,-118.5 491,-112.5 335,-112.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="336,-113.5 490,-113.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="490,-117.5 336,-117.5 "/>
<text text-anchor="start" x="348.5" y="-99.3" font-family="arial" font-size="14.00">Key:R</text>
<text text-anchor="start" x="422" y="-99.3" font-family="arial" font-size="14.00">BN</text>
<polygon fill="#666600" stroke="transparent" stroke-width="2" points="335,-87.5 335,-93.5 491,-93.5 491,-87.5 335,-87.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="336,-88.5 490,-88.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="490,-92.5 336,-92.5 "/>
<text text-anchor="start" x="349" y="-74.3" font-family="arial" font-size="14.00">Key:T</text>
<text text-anchor="start" x="420.5" y="-74.3" font-family="arial" font-size="14.00">GN</text>
<polygon fill="#00ff00" stroke="transparent" stroke-width="2" points="335,-62.5 335,-68.5 491,-68.5 491,-62.5 335,-62.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="336,-63.5 490,-63.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="490,-67.5 336,-67.5 "/>
<text text-anchor="start" x="365" y="-49.3" font-family="arial" font-size="14.00"> </text>
<text text-anchor="start" x="348.5" y="-30.3" font-family="arial" font-size="14.00">Key:S</text>
<text text-anchor="start" x="412.5" y="-30.3" font-family="arial" font-size="14.00">Shield</text>
<polyline fill="none" stroke="black" stroke-width="2" points="336,-19.5 490,-19.5 "/>
<text text-anchor="start" x="365" y="-5.3" font-family="arial" font-size="14.00"> </text>
</g>
<!-- Key&#45;&#45;W1 -->
<g id="edge1" class="edge">
<title>Key:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-42.5C264.36,-44.46 265.64,-115.46 335,-113.5"/>
<path fill="none" stroke="#ffffff" stroke-width="2" d="M191,-44.5C262.36,-44.5 263.64,-115.5 335,-115.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-46.5C260.36,-44.54 261.64,-115.54 335,-117.5"/>
</g>
<!-- Key&#45;&#45;W1 -->
<g id="edge2" class="edge">
<title>Key:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-65.5C257.51,-66.44 271.88,-89.44 335,-88.5"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M191,-67.5C255.81,-67.5 270.19,-90.5 335,-90.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-69.5C254.12,-68.56 268.49,-91.56 335,-92.5"/>
</g>
<!-- Key&#45;&#45;W1 -->
<g id="edge3" class="edge">
<title>Key:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-88.5C254.21,-89.52 268.3,-64.52 335,-63.5"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M191,-90.5C255.96,-90.5 270.04,-65.5 335,-65.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-92.5C257.7,-91.48 271.79,-66.48 335,-67.5"/>
</g>
<!-- Key&#45;&#45;W1 -->
<g id="edge4" class="edge">
<title>Key:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M191,-44.5C255.81,-44.5 270.19,-21.5 335,-21.5"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

25
examples/ex08.yml Normal file
View File

@ -0,0 +1,25 @@
# contributed by @cocide
connectors:
Key:
type: Phone Connector
subtype: male 3.5
pinnumbers: [T, R, S]
pinout: [Dot, Dash, Ground]
show_pincount: false
cables:
W1:
gauge: 24 AWG
length: 0.2
color_code: DIN
wirecount: 3
shield: true
connections:
-
- Key: [S,R,T]
- W1: [1,2,3]
-
- Key: S
- W1: s

4
examples/ex09.bom.tsv Normal file
View File

@ -0,0 +1,4 @@
Item Qty Unit Designators
Connector, D-Sub, male, 25 pins 1 X1
Connector, F48, female, 48 pins 1 X2
Cable, 12 x 0.25 mm² shielded 0.2 m W1
1 Item Qty Unit Designators
2 Connector, D-Sub, male, 25 pins 1 X1
3 Connector, F48, female, 48 pins 1 X2
4 Cable, 12 x 0.25 mm² shielded 0.2 m W1

48
examples/ex09.gv Normal file
View File

@ -0,0 +1,48 @@
graph {
// Graph generated by WireViz
// https://github.com/formatc1702/WireViz
graph [bgcolor=white fontname=arial nodesep=0.33 rankdir=LR ranksep=2]
node [fillcolor=white fontname=arial shape=record style=filled]
edge [fontname=arial style=bold]
X1 [label="X1|{D-Sub|male|25-pin}|{{SENSE_P_1|SENSE_N_1|SENSE_P_2|SENSE_N_2|SENSE_P_3|SENSE_N_3|SENSE_P_4|SENSE_N_4|SENSE_P_5|SENSE_N_5|SENSE_P_6|SENSE_N_6|GND}|{<p1r>1|<p14r>14|<p3r>3|<p16r>16|<p5r>5|<p18r>18|<p7r>7|<p20r>20|<p9r>9|<p22r>22|<p11r>11|<p24r>24|<p13r>13}}"]
X2 [label="X2|{F48|female|48-pin}|{{<pz2l>z2|<pb2l>b2|<pd2l>d2|<pz4l>z4|<pb4l>b4|<pd4l>d4|<pz6l>z6|<pb6l>b6|<pd6l>d6|<pz8l>z8|<pb8l>b8|<pd8l>d8|<pz10l>z10|<pb10l>b10|<pd10l>d10|<pz12l>z12|<pb12l>b12|<pd12l>d12|<pz14l>z14|<pb14l>b14|<pd14l>d14|<pz16l>z16|<pb16l>b16|<pd16l>d16|<pz18l>z18|<pb18l>b18|<pd18l>d18|<pz20l>z20|<pb20l>b20|<pd20l>d20|<pz22l>z22|<pb22l>b22|<pd22l>d22|<pz24l>z24|<pb24l>b24|<pd24l>d24|<pz26l>z26|<pb26l>b26|<pd26l>d26|<pz28l>z28|<pb28l>b28|<pd28l>d28|<pz30l>z30|<pb30l>b30|<pd30l>d30|<pz32l>z32|<pb32l>b32|<pd32l>d32}}"]
edge [color="#000000:#666600:#000000"]
X1:p1r:e -- W1:w2:w
W1:w2:e -- X2:pd4l:w
edge [color="#000000:#ffffff:#000000"]
X1:p14r:e -- W1:w1:w
W1:w1:e -- X2:pz2l:w
edge [color="#000000:#ffff00:#000000"]
X1:p3r:e -- W1:w4:w
W1:w4:e -- X2:pd10l:w
edge [color="#000000:#00ff00:#000000"]
X1:p16r:e -- W1:w3:w
W1:w3:e -- X2:pz8l:w
edge [color="#000000:#ff66cc:#000000"]
X1:p5r:e -- W1:w6:w
W1:w6:e -- X2:pd16l:w
edge [color="#000000:#999999:#000000"]
X1:p18r:e -- W1:w5:w
W1:w5:e -- X2:pz14l:w
edge [color="#000000:#ff0000:#000000"]
X1:p7r:e -- W1:w8:w
W1:w8:e -- X2:pd20l:w
edge [color="#000000:#0066ff:#000000"]
X1:p20r:e -- W1:w7:w
W1:w7:e -- X2:pz18l:w
edge [color="#000000:#8000ff:#000000"]
X1:p9r:e -- W1:w10:w
W1:w10:e -- X2:pd26l:w
edge [color="#000000:#000000:#000000"]
X1:p22r:e -- W1:w9:w
W1:w9:e -- X2:pz24l:w
edge [color="#000000:#666600:#000000"]
X1:p11r:e -- W1:w12:w
W1:w12:e -- X2:pd32l:w
edge [color="#000000:#ffffff:#000000"]
X1:p24r:e -- W1:w11:w
W1:w11:e -- X2:pz30l:w
edge [color="#000000"]
X1:p13r:e -- W1:ws:w
W1 [label=<<table border="0" cellspacing="0" cellpadding="0"><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td colspan="4">W1</td></tr><tr><td>12x</td><td>0.25 mm²</td><td>+ S</td><td>0.2 m</td></tr></table></td></tr><tr><td>&nbsp;</td></tr><tr><td><table border="0" cellspacing="0" cellborder="0"><tr><td>X1:14</td><td>WH</td><td>X2:z2</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ffffff" border="2" sides="tb" port="w1"></td></tr><tr><td>X1:1</td><td>BN</td><td>X2:d4</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#666600" border="2" sides="tb" port="w2"></td></tr><tr><td>X1:16</td><td>GN</td><td>X2:z8</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#00ff00" border="2" sides="tb" port="w3"></td></tr><tr><td>X1:3</td><td>YE</td><td>X2:d10</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ffff00" border="2" sides="tb" port="w4"></td></tr><tr><td>X1:18</td><td>GY</td><td>X2:z14</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#999999" border="2" sides="tb" port="w5"></td></tr><tr><td>X1:5</td><td>PK</td><td>X2:d16</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ff66cc" border="2" sides="tb" port="w6"></td></tr><tr><td>X1:20</td><td>BU</td><td>X2:z18</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#0066ff" border="2" sides="tb" port="w7"></td></tr><tr><td>X1:7</td><td>RD</td><td>X2:d20</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ff0000" border="2" sides="tb" port="w8"></td></tr><tr><td>X1:22</td><td>BK</td><td>X2:z24</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#000000" border="2" sides="tb" port="w9"></td></tr><tr><td>X1:9</td><td>VT</td><td>X2:d26</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#8000ff" border="2" sides="tb" port="w10"></td></tr><tr><td>X1:24</td><td>WH</td><td>X2:z30</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#ffffff" border="2" sides="tb" port="w11"></td></tr><tr><td>X1:11</td><td>BN</td><td>X2:d32</td></tr><tr><td colspan="3" cellpadding="0" height="6" bgcolor="#666600" border="2" sides="tb" port="w12"></td></tr><tr><td>&nbsp;</td></tr><tr><td>X1:13</td><td>Shield</td><td><!-- s_out --></td></tr><tr><td colspan="3" cellpadding="0" height="6" border="2" sides="b" port="ws"></td></tr><tr><td>&nbsp;</td></tr></table></td></tr></table>> fillcolor=white margin=0 shape=box style=""]
}

451
examples/ex09.html Normal file
View File

@ -0,0 +1,451 @@
<html><body style="font-family:Arial"><h1>Diagram</h1><?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.44.0 (20200408.0750)
-->
<!-- Pages: 1 -->
<svg width="774pt" height="1159pt"
viewBox="0.00 0.00 774.00 1159.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1155)">
<polygon fill="white" stroke="transparent" points="-4,4 -4,-1155 770,-1155 770,4 -4,4"/>
<!-- X1 -->
<g id="node1" class="node">
<title>X1</title>
<polygon fill="white" stroke="black" points="0,-377 0,-722 156,-722 156,-377 0,-377"/>
<text text-anchor="middle" x="78" y="-706.8" font-family="arial" font-size="14.00">X1</text>
<polyline fill="none" stroke="black" points="0,-699 156,-699 "/>
<text text-anchor="middle" x="27.5" y="-683.8" font-family="arial" font-size="14.00">D&#45;Sub</text>
<polyline fill="none" stroke="black" points="55,-676 55,-699 "/>
<text text-anchor="middle" x="78.5" y="-683.8" font-family="arial" font-size="14.00">male</text>
<polyline fill="none" stroke="black" points="102,-676 102,-699 "/>
<text text-anchor="middle" x="129" y="-683.8" font-family="arial" font-size="14.00">25&#45;pin</text>
<polyline fill="none" stroke="black" points="0,-676 156,-676 "/>
<text text-anchor="middle" x="54.5" y="-660.8" font-family="arial" font-size="14.00">SENSE_P_1</text>
<polyline fill="none" stroke="black" points="0,-653 109,-653 "/>
<text text-anchor="middle" x="54.5" y="-637.8" font-family="arial" font-size="14.00">SENSE_N_1</text>
<polyline fill="none" stroke="black" points="0,-630 109,-630 "/>
<text text-anchor="middle" x="54.5" y="-614.8" font-family="arial" font-size="14.00">SENSE_P_2</text>
<polyline fill="none" stroke="black" points="0,-607 109,-607 "/>
<text text-anchor="middle" x="54.5" y="-591.8" font-family="arial" font-size="14.00">SENSE_N_2</text>
<polyline fill="none" stroke="black" points="0,-584 109,-584 "/>
<text text-anchor="middle" x="54.5" y="-568.8" font-family="arial" font-size="14.00">SENSE_P_3</text>
<polyline fill="none" stroke="black" points="0,-561 109,-561 "/>
<text text-anchor="middle" x="54.5" y="-545.8" font-family="arial" font-size="14.00">SENSE_N_3</text>
<polyline fill="none" stroke="black" points="0,-538 109,-538 "/>
<text text-anchor="middle" x="54.5" y="-522.8" font-family="arial" font-size="14.00">SENSE_P_4</text>
<polyline fill="none" stroke="black" points="0,-515 109,-515 "/>
<text text-anchor="middle" x="54.5" y="-499.8" font-family="arial" font-size="14.00">SENSE_N_4</text>
<polyline fill="none" stroke="black" points="0,-492 109,-492 "/>
<text text-anchor="middle" x="54.5" y="-476.8" font-family="arial" font-size="14.00">SENSE_P_5</text>
<polyline fill="none" stroke="black" points="0,-469 109,-469 "/>
<text text-anchor="middle" x="54.5" y="-453.8" font-family="arial" font-size="14.00">SENSE_N_5</text>
<polyline fill="none" stroke="black" points="0,-446 109,-446 "/>
<text text-anchor="middle" x="54.5" y="-430.8" font-family="arial" font-size="14.00">SENSE_P_6</text>
<polyline fill="none" stroke="black" points="0,-423 109,-423 "/>
<text text-anchor="middle" x="54.5" y="-407.8" font-family="arial" font-size="14.00">SENSE_N_6</text>
<polyline fill="none" stroke="black" points="0,-400 109,-400 "/>
<text text-anchor="middle" x="54.5" y="-384.8" font-family="arial" font-size="14.00">GND</text>
<polyline fill="none" stroke="black" points="109,-377 109,-676 "/>
<text text-anchor="middle" x="132.5" y="-660.8" font-family="arial" font-size="14.00">1</text>
<polyline fill="none" stroke="black" points="109,-653 156,-653 "/>
<text text-anchor="middle" x="132.5" y="-637.8" font-family="arial" font-size="14.00">14</text>
<polyline fill="none" stroke="black" points="109,-630 156,-630 "/>
<text text-anchor="middle" x="132.5" y="-614.8" font-family="arial" font-size="14.00">3</text>
<polyline fill="none" stroke="black" points="109,-607 156,-607 "/>
<text text-anchor="middle" x="132.5" y="-591.8" font-family="arial" font-size="14.00">16</text>
<polyline fill="none" stroke="black" points="109,-584 156,-584 "/>
<text text-anchor="middle" x="132.5" y="-568.8" font-family="arial" font-size="14.00">5</text>
<polyline fill="none" stroke="black" points="109,-561 156,-561 "/>
<text text-anchor="middle" x="132.5" y="-545.8" font-family="arial" font-size="14.00">18</text>
<polyline fill="none" stroke="black" points="109,-538 156,-538 "/>
<text text-anchor="middle" x="132.5" y="-522.8" font-family="arial" font-size="14.00">7</text>
<polyline fill="none" stroke="black" points="109,-515 156,-515 "/>
<text text-anchor="middle" x="132.5" y="-499.8" font-family="arial" font-size="14.00">20</text>
<polyline fill="none" stroke="black" points="109,-492 156,-492 "/>
<text text-anchor="middle" x="132.5" y="-476.8" font-family="arial" font-size="14.00">9</text>
<polyline fill="none" stroke="black" points="109,-469 156,-469 "/>
<text text-anchor="middle" x="132.5" y="-453.8" font-family="arial" font-size="14.00">22</text>
<polyline fill="none" stroke="black" points="109,-446 156,-446 "/>
<text text-anchor="middle" x="132.5" y="-430.8" font-family="arial" font-size="14.00">11</text>
<polyline fill="none" stroke="black" points="109,-423 156,-423 "/>
<text text-anchor="middle" x="132.5" y="-407.8" font-family="arial" font-size="14.00">24</text>
<polyline fill="none" stroke="black" points="109,-400 156,-400 "/>
<text text-anchor="middle" x="132.5" y="-384.8" font-family="arial" font-size="14.00">13</text>
</g>
<!-- W1 -->
<g id="node3" class="node">
<title>W1</title>
<polygon fill="none" stroke="black" points="470,-772.5 300,-772.5 300,-348.5 470,-348.5 470,-772.5"/>
<polygon fill="none" stroke="black" points="300,-749.5 300,-772.5 470,-772.5 470,-749.5 300,-749.5"/>
<text text-anchor="start" x="374" y="-757.3" font-family="arial" font-size="14.00">W1</text>
<polygon fill="none" stroke="black" points="300,-726.5 300,-749.5 330,-749.5 330,-726.5 300,-726.5"/>
<text text-anchor="start" x="304" y="-734.3" font-family="arial" font-size="14.00">12x</text>
<polygon fill="none" stroke="black" points="330,-726.5 330,-749.5 397,-749.5 397,-726.5 330,-726.5"/>
<text text-anchor="start" x="334" y="-734.3" font-family="arial" font-size="14.00">0.25 mm²</text>
<polygon fill="none" stroke="black" points="397,-726.5 397,-749.5 427,-749.5 427,-726.5 397,-726.5"/>
<text text-anchor="start" x="401" y="-734.3" font-family="arial" font-size="14.00">+ S</text>
<polygon fill="none" stroke="black" points="427,-726.5 427,-749.5 470,-749.5 470,-726.5 427,-726.5"/>
<text text-anchor="start" x="431" y="-734.3" font-family="arial" font-size="14.00">0.2 m</text>
<text text-anchor="start" x="383" y="-715.3" font-family="arial" font-size="14.00"> </text>
<text text-anchor="start" x="309" y="-698.3" font-family="arial" font-size="14.00">X1:14</text>
<text text-anchor="start" x="370" y="-698.3" font-family="arial" font-size="14.00">WH</text>
<text text-anchor="start" x="422.5" y="-698.3" font-family="arial" font-size="14.00">X2:z2</text>
<polygon fill="#ffffff" stroke="transparent" points="300,-686.5 300,-692.5 470,-692.5 470,-686.5 300,-686.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-687.5 469,-687.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-691.5 301,-691.5 "/>
<text text-anchor="start" x="313" y="-673.3" font-family="arial" font-size="14.00">X1:1</text>
<text text-anchor="start" x="372.5" y="-673.3" font-family="arial" font-size="14.00">BN</text>
<text text-anchor="start" x="422" y="-673.3" font-family="arial" font-size="14.00">X2:d4</text>
<polygon fill="#666600" stroke="transparent" stroke-width="2" points="300,-661.5 300,-667.5 470,-667.5 470,-661.5 300,-661.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-662.5 469,-662.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-666.5 301,-666.5 "/>
<text text-anchor="start" x="309" y="-648.3" font-family="arial" font-size="14.00">X1:16</text>
<text text-anchor="start" x="371" y="-648.3" font-family="arial" font-size="14.00">GN</text>
<text text-anchor="start" x="422.5" y="-648.3" font-family="arial" font-size="14.00">X2:z8</text>
<polygon fill="#00ff00" stroke="transparent" stroke-width="2" points="300,-636.5 300,-642.5 470,-642.5 470,-636.5 300,-636.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-637.5 469,-637.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-641.5 301,-641.5 "/>
<text text-anchor="start" x="313" y="-623.3" font-family="arial" font-size="14.00">X1:3</text>
<text text-anchor="start" x="372.5" y="-623.3" font-family="arial" font-size="14.00">YE</text>
<text text-anchor="start" x="418.5" y="-623.3" font-family="arial" font-size="14.00">X2:d10</text>
<polygon fill="#ffff00" stroke="transparent" stroke-width="2" points="300,-611.5 300,-617.5 470,-617.5 470,-611.5 300,-611.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-612.5 469,-612.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-616.5 301,-616.5 "/>
<text text-anchor="start" x="309" y="-598.3" font-family="arial" font-size="14.00">X1:18</text>
<text text-anchor="start" x="371.5" y="-598.3" font-family="arial" font-size="14.00">GY</text>
<text text-anchor="start" x="418.5" y="-598.3" font-family="arial" font-size="14.00">X2:z14</text>
<polygon fill="#999999" stroke="transparent" stroke-width="2" points="300,-586.5 300,-592.5 470,-592.5 470,-586.5 300,-586.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-587.5 469,-587.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-591.5 301,-591.5 "/>
<text text-anchor="start" x="313" y="-573.3" font-family="arial" font-size="14.00">X1:5</text>
<text text-anchor="start" x="372.5" y="-573.3" font-family="arial" font-size="14.00">PK</text>
<text text-anchor="start" x="418.5" y="-573.3" font-family="arial" font-size="14.00">X2:d16</text>
<polygon fill="#ff66cc" stroke="transparent" stroke-width="2" points="300,-561.5 300,-567.5 470,-567.5 470,-561.5 300,-561.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-562.5 469,-562.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-566.5 301,-566.5 "/>
<text text-anchor="start" x="309" y="-548.3" font-family="arial" font-size="14.00">X1:20</text>
<text text-anchor="start" x="372.5" y="-548.3" font-family="arial" font-size="14.00">BU</text>
<text text-anchor="start" x="418.5" y="-548.3" font-family="arial" font-size="14.00">X2:z18</text>
<polygon fill="#0066ff" stroke="transparent" stroke-width="2" points="300,-536.5 300,-542.5 470,-542.5 470,-536.5 300,-536.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-537.5 469,-537.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-541.5 301,-541.5 "/>
<text text-anchor="start" x="313" y="-523.3" font-family="arial" font-size="14.00">X1:7</text>
<text text-anchor="start" x="372" y="-523.3" font-family="arial" font-size="14.00">RD</text>
<text text-anchor="start" x="418.5" y="-523.3" font-family="arial" font-size="14.00">X2:d20</text>
<polygon fill="#ff0000" stroke="transparent" stroke-width="2" points="300,-511.5 300,-517.5 470,-517.5 470,-511.5 300,-511.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-512.5 469,-512.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-516.5 301,-516.5 "/>
<text text-anchor="start" x="309" y="-498.3" font-family="arial" font-size="14.00">X1:22</text>
<text text-anchor="start" x="372.5" y="-498.3" font-family="arial" font-size="14.00">BK</text>
<text text-anchor="start" x="418.5" y="-498.3" font-family="arial" font-size="14.00">X2:z24</text>
<polygon fill="#000000" stroke="transparent" stroke-width="2" points="300,-486.5 300,-492.5 470,-492.5 470,-486.5 300,-486.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-487.5 469,-487.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-491.5 301,-491.5 "/>
<text text-anchor="start" x="313" y="-473.3" font-family="arial" font-size="14.00">X1:9</text>
<text text-anchor="start" x="373" y="-473.3" font-family="arial" font-size="14.00">VT</text>
<text text-anchor="start" x="418.5" y="-473.3" font-family="arial" font-size="14.00">X2:d26</text>
<polygon fill="#8000ff" stroke="transparent" stroke-width="2" points="300,-461.5 300,-467.5 470,-467.5 470,-461.5 300,-461.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-462.5 469,-462.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-466.5 301,-466.5 "/>
<text text-anchor="start" x="309" y="-448.3" font-family="arial" font-size="14.00">X1:24</text>
<text text-anchor="start" x="370" y="-448.3" font-family="arial" font-size="14.00">WH</text>
<text text-anchor="start" x="418.5" y="-448.3" font-family="arial" font-size="14.00">X2:z30</text>
<polygon fill="#ffffff" stroke="transparent" stroke-width="2" points="300,-436.5 300,-442.5 470,-442.5 470,-436.5 300,-436.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-437.5 469,-437.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-441.5 301,-441.5 "/>
<text text-anchor="start" x="309.5" y="-423.3" font-family="arial" font-size="14.00">X1:11</text>
<text text-anchor="start" x="372.5" y="-423.3" font-family="arial" font-size="14.00">BN</text>
<text text-anchor="start" x="418.5" y="-423.3" font-family="arial" font-size="14.00">X2:d32</text>
<polygon fill="#666600" stroke="transparent" stroke-width="2" points="300,-411.5 300,-417.5 470,-417.5 470,-411.5 300,-411.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-412.5 469,-412.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-416.5 301,-416.5 "/>
<text text-anchor="start" x="325" y="-398.3" font-family="arial" font-size="14.00"> </text>
<text text-anchor="start" x="309" y="-379.3" font-family="arial" font-size="14.00">X1:13</text>
<text text-anchor="start" x="363" y="-379.3" font-family="arial" font-size="14.00">Shield</text>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-368.5 469,-368.5 "/>
<text text-anchor="start" x="325" y="-354.3" font-family="arial" font-size="14.00"> </text>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge1" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-662.5C220,-662.5 236,-662.5 300,-662.5"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M156,-664.5C220,-664.5 236,-664.5 300,-664.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-666.5C220,-666.5 236,-666.5 300,-666.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge3" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-639.5C225.43,-641.13 234.5,-689.13 300,-687.5"/>
<path fill="none" stroke="#ffffff" stroke-width="2" d="M156,-641.5C223.46,-641.5 232.54,-689.5 300,-689.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-643.5C221.5,-641.87 230.57,-689.87 300,-691.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge5" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-616.5C219.54,-616.56 235.49,-612.56 300,-612.5"/>
<path fill="none" stroke="#ffff00" stroke-width="2" d="M156,-618.5C220.02,-618.5 235.98,-614.5 300,-614.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-620.5C220.51,-620.44 236.46,-616.44 300,-616.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge7" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-593.5C224.87,-595.05 235.03,-639.05 300,-637.5"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M156,-595.5C222.92,-595.5 233.08,-639.5 300,-639.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-597.5C220.97,-595.95 231.13,-639.95 300,-641.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge9" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-570.5C219.2,-570.72 235,-562.72 300,-562.5"/>
<path fill="none" stroke="#ff66cc" stroke-width="2" d="M156,-572.5C220.1,-572.5 235.9,-564.5 300,-564.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-574.5C221,-574.28 236.8,-566.28 300,-566.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge11" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-547.5C224.35,-548.96 235.5,-588.96 300,-587.5"/>
<path fill="none" stroke="#999999" stroke-width="2" d="M156,-549.5C222.42,-549.5 233.58,-589.5 300,-589.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-551.5C220.5,-550.04 231.65,-590.04 300,-591.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge13" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-524.5C219,-524.92 234.56,-512.92 300,-512.5"/>
<path fill="none" stroke="#ff0000" stroke-width="2" d="M156,-526.5C220.22,-526.5 235.78,-514.5 300,-514.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-528.5C221.44,-528.08 237,-516.08 300,-516.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge15" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-501.5C223.87,-502.86 235.93,-538.86 300,-537.5"/>
<path fill="none" stroke="#0066ff" stroke-width="2" d="M156,-503.5C221.97,-503.5 234.03,-539.5 300,-539.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-505.5C220.07,-504.14 232.13,-540.14 300,-541.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge17" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-478.5C218.94,-479.12 234.16,-463.12 300,-462.5"/>
<path fill="none" stroke="#8000ff" stroke-width="2" d="M156,-480.5C220.39,-480.5 235.61,-464.5 300,-464.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-482.5C221.84,-481.88 237.06,-465.88 300,-466.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge19" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-455.5C223.42,-456.75 236.29,-488.75 300,-487.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-457.5C221.56,-457.5 234.44,-489.5 300,-489.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-459.5C219.71,-458.25 232.58,-490.25 300,-491.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge21" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-432.5C219.01,-433.31 233.78,-413.31 300,-412.5"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M156,-434.5C220.61,-434.5 235.39,-414.5 300,-414.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-436.5C222.22,-435.69 236.99,-415.69 300,-416.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge23" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-409.5C223,-410.63 236.6,-438.63 300,-437.5"/>
<path fill="none" stroke="#ffffff" stroke-width="2" d="M156,-411.5C221.2,-411.5 234.8,-439.5 300,-439.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-413.5C219.4,-412.37 233,-440.37 300,-441.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge25" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-388.5C220.5,-388.5 235.5,-370.5 300,-370.5"/>
</g>
<!-- X2 -->
<g id="node2" class="node">
<title>X2</title>
<polygon fill="white" stroke="black" points="614,-0.5 614,-1150.5 766,-1150.5 766,-0.5 614,-0.5"/>
<text text-anchor="middle" x="690" y="-1135.3" font-family="arial" font-size="14.00">X2</text>
<polyline fill="none" stroke="black" points="614,-1127.5 766,-1127.5 "/>
<text text-anchor="middle" x="634" y="-1112.3" font-family="arial" font-size="14.00">F48</text>
<polyline fill="none" stroke="black" points="654,-1104.5 654,-1127.5 "/>
<text text-anchor="middle" x="683" y="-1112.3" font-family="arial" font-size="14.00">female</text>
<polyline fill="none" stroke="black" points="712,-1104.5 712,-1127.5 "/>
<text text-anchor="middle" x="739" y="-1112.3" font-family="arial" font-size="14.00">48&#45;pin</text>
<polyline fill="none" stroke="black" points="614,-1104.5 766,-1104.5 "/>
<text text-anchor="middle" x="690" y="-1089.3" font-family="arial" font-size="14.00">z2</text>
<polyline fill="none" stroke="black" points="614,-1081.5 766,-1081.5 "/>
<text text-anchor="middle" x="690" y="-1066.3" font-family="arial" font-size="14.00">b2</text>
<polyline fill="none" stroke="black" points="614,-1058.5 766,-1058.5 "/>
<text text-anchor="middle" x="690" y="-1043.3" font-family="arial" font-size="14.00">d2</text>
<polyline fill="none" stroke="black" points="614,-1035.5 766,-1035.5 "/>
<text text-anchor="middle" x="690" y="-1020.3" font-family="arial" font-size="14.00">z4</text>
<polyline fill="none" stroke="black" points="614,-1012.5 766,-1012.5 "/>
<text text-anchor="middle" x="690" y="-997.3" font-family="arial" font-size="14.00">b4</text>
<polyline fill="none" stroke="black" points="614,-989.5 766,-989.5 "/>
<text text-anchor="middle" x="690" y="-974.3" font-family="arial" font-size="14.00">d4</text>
<polyline fill="none" stroke="black" points="614,-966.5 766,-966.5 "/>
<text text-anchor="middle" x="690" y="-951.3" font-family="arial" font-size="14.00">z6</text>
<polyline fill="none" stroke="black" points="614,-943.5 766,-943.5 "/>
<text text-anchor="middle" x="690" y="-928.3" font-family="arial" font-size="14.00">b6</text>
<polyline fill="none" stroke="black" points="614,-920.5 766,-920.5 "/>
<text text-anchor="middle" x="690" y="-905.3" font-family="arial" font-size="14.00">d6</text>
<polyline fill="none" stroke="black" points="614,-897.5 766,-897.5 "/>
<text text-anchor="middle" x="690" y="-882.3" font-family="arial" font-size="14.00">z8</text>
<polyline fill="none" stroke="black" points="614,-874.5 766,-874.5 "/>
<text text-anchor="middle" x="690" y="-859.3" font-family="arial" font-size="14.00">b8</text>
<polyline fill="none" stroke="black" points="614,-851.5 766,-851.5 "/>
<text text-anchor="middle" x="690" y="-836.3" font-family="arial" font-size="14.00">d8</text>
<polyline fill="none" stroke="black" points="614,-828.5 766,-828.5 "/>
<text text-anchor="middle" x="690" y="-813.3" font-family="arial" font-size="14.00">z10</text>
<polyline fill="none" stroke="black" points="614,-805.5 766,-805.5 "/>
<text text-anchor="middle" x="690" y="-790.3" font-family="arial" font-size="14.00">b10</text>
<polyline fill="none" stroke="black" points="614,-782.5 766,-782.5 "/>
<text text-anchor="middle" x="690" y="-767.3" font-family="arial" font-size="14.00">d10</text>
<polyline fill="none" stroke="black" points="614,-759.5 766,-759.5 "/>
<text text-anchor="middle" x="690" y="-744.3" font-family="arial" font-size="14.00">z12</text>
<polyline fill="none" stroke="black" points="614,-736.5 766,-736.5 "/>
<text text-anchor="middle" x="690" y="-721.3" font-family="arial" font-size="14.00">b12</text>
<polyline fill="none" stroke="black" points="614,-713.5 766,-713.5 "/>
<text text-anchor="middle" x="690" y="-698.3" font-family="arial" font-size="14.00">d12</text>
<polyline fill="none" stroke="black" points="614,-690.5 766,-690.5 "/>
<text text-anchor="middle" x="690" y="-675.3" font-family="arial" font-size="14.00">z14</text>
<polyline fill="none" stroke="black" points="614,-667.5 766,-667.5 "/>
<text text-anchor="middle" x="690" y="-652.3" font-family="arial" font-size="14.00">b14</text>
<polyline fill="none" stroke="black" points="614,-644.5 766,-644.5 "/>
<text text-anchor="middle" x="690" y="-629.3" font-family="arial" font-size="14.00">d14</text>
<polyline fill="none" stroke="black" points="614,-621.5 766,-621.5 "/>
<text text-anchor="middle" x="690" y="-606.3" font-family="arial" font-size="14.00">z16</text>
<polyline fill="none" stroke="black" points="614,-598.5 766,-598.5 "/>
<text text-anchor="middle" x="690" y="-583.3" font-family="arial" font-size="14.00">b16</text>
<polyline fill="none" stroke="black" points="614,-575.5 766,-575.5 "/>
<text text-anchor="middle" x="690" y="-560.3" font-family="arial" font-size="14.00">d16</text>
<polyline fill="none" stroke="black" points="614,-552.5 766,-552.5 "/>
<text text-anchor="middle" x="690" y="-537.3" font-family="arial" font-size="14.00">z18</text>
<polyline fill="none" stroke="black" points="614,-529.5 766,-529.5 "/>
<text text-anchor="middle" x="690" y="-514.3" font-family="arial" font-size="14.00">b18</text>
<polyline fill="none" stroke="black" points="614,-506.5 766,-506.5 "/>
<text text-anchor="middle" x="690" y="-491.3" font-family="arial" font-size="14.00">d18</text>
<polyline fill="none" stroke="black" points="614,-483.5 766,-483.5 "/>
<text text-anchor="middle" x="690" y="-468.3" font-family="arial" font-size="14.00">z20</text>
<polyline fill="none" stroke="black" points="614,-460.5 766,-460.5 "/>
<text text-anchor="middle" x="690" y="-445.3" font-family="arial" font-size="14.00">b20</text>
<polyline fill="none" stroke="black" points="614,-437.5 766,-437.5 "/>
<text text-anchor="middle" x="690" y="-422.3" font-family="arial" font-size="14.00">d20</text>
<polyline fill="none" stroke="black" points="614,-414.5 766,-414.5 "/>
<text text-anchor="middle" x="690" y="-399.3" font-family="arial" font-size="14.00">z22</text>
<polyline fill="none" stroke="black" points="614,-391.5 766,-391.5 "/>
<text text-anchor="middle" x="690" y="-376.3" font-family="arial" font-size="14.00">b22</text>
<polyline fill="none" stroke="black" points="614,-368.5 766,-368.5 "/>
<text text-anchor="middle" x="690" y="-353.3" font-family="arial" font-size="14.00">d22</text>
<polyline fill="none" stroke="black" points="614,-345.5 766,-345.5 "/>
<text text-anchor="middle" x="690" y="-330.3" font-family="arial" font-size="14.00">z24</text>
<polyline fill="none" stroke="black" points="614,-322.5 766,-322.5 "/>
<text text-anchor="middle" x="690" y="-307.3" font-family="arial" font-size="14.00">b24</text>
<polyline fill="none" stroke="black" points="614,-299.5 766,-299.5 "/>
<text text-anchor="middle" x="690" y="-284.3" font-family="arial" font-size="14.00">d24</text>
<polyline fill="none" stroke="black" points="614,-276.5 766,-276.5 "/>
<text text-anchor="middle" x="690" y="-261.3" font-family="arial" font-size="14.00">z26</text>
<polyline fill="none" stroke="black" points="614,-253.5 766,-253.5 "/>
<text text-anchor="middle" x="690" y="-238.3" font-family="arial" font-size="14.00">b26</text>
<polyline fill="none" stroke="black" points="614,-230.5 766,-230.5 "/>
<text text-anchor="middle" x="690" y="-215.3" font-family="arial" font-size="14.00">d26</text>
<polyline fill="none" stroke="black" points="614,-207.5 766,-207.5 "/>
<text text-anchor="middle" x="690" y="-192.3" font-family="arial" font-size="14.00">z28</text>
<polyline fill="none" stroke="black" points="614,-184.5 766,-184.5 "/>
<text text-anchor="middle" x="690" y="-169.3" font-family="arial" font-size="14.00">b28</text>
<polyline fill="none" stroke="black" points="614,-161.5 766,-161.5 "/>
<text text-anchor="middle" x="690" y="-146.3" font-family="arial" font-size="14.00">d28</text>
<polyline fill="none" stroke="black" points="614,-138.5 766,-138.5 "/>
<text text-anchor="middle" x="690" y="-123.3" font-family="arial" font-size="14.00">z30</text>
<polyline fill="none" stroke="black" points="614,-115.5 766,-115.5 "/>
<text text-anchor="middle" x="690" y="-100.3" font-family="arial" font-size="14.00">b30</text>
<polyline fill="none" stroke="black" points="614,-92.5 766,-92.5 "/>
<text text-anchor="middle" x="690" y="-77.3" font-family="arial" font-size="14.00">d30</text>
<polyline fill="none" stroke="black" points="614,-69.5 766,-69.5 "/>
<text text-anchor="middle" x="690" y="-54.3" font-family="arial" font-size="14.00">z32</text>
<polyline fill="none" stroke="black" points="614,-46.5 766,-46.5 "/>
<text text-anchor="middle" x="690" y="-31.3" font-family="arial" font-size="14.00">b32</text>
<polyline fill="none" stroke="black" points="614,-23.5 766,-23.5 "/>
<text text-anchor="middle" x="690" y="-8.3" font-family="arial" font-size="14.00">d32</text>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge2" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-662.5C625.31,-665.42 462.24,-979.42 614,-976.5"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M470,-664.5C623.53,-664.5 460.47,-978.5 614,-978.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-666.5C621.76,-663.58 458.69,-977.58 614,-980.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge4" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-687.5C662.35,-690.51 425.1,-1094.51 614,-1091.5"/>
<path fill="none" stroke="#ffffff" stroke-width="2" d="M470,-689.5C660.62,-689.5 423.38,-1093.5 614,-1093.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-691.5C658.9,-688.49 421.65,-1092.49 614,-1095.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge6" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-612.5C566.6,-615.06 521.24,-772.06 614,-769.5"/>
<path fill="none" stroke="#ffff00" stroke-width="2" d="M470,-614.5C564.68,-614.5 519.32,-771.5 614,-771.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-616.5C562.76,-613.94 517.4,-770.94 614,-773.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge8" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-637.5C598.9,-640.31 488.76,-887.31 614,-884.5"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M470,-639.5C597.07,-639.5 486.93,-886.5 614,-886.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-641.5C595.24,-638.69 485.1,-885.69 614,-888.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge10" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-562.5C533.88,-562.5 549.87,-561.5 614,-561.5"/>
<path fill="none" stroke="#ff66cc" stroke-width="2" d="M470,-564.5C534,-564.5 550,-563.5 614,-563.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-566.5C534.13,-566.5 550.12,-565.5 614,-565.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge12" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-587.5C547.47,-589.65 540.52,-679.65 614,-677.5"/>
<path fill="none" stroke="#999999" stroke-width="2" d="M470,-589.5C545.47,-589.5 538.53,-679.5 614,-679.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-591.5C543.48,-589.35 536.53,-679.35 614,-681.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge14" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-512.5C543.24,-514.65 536.77,-425.65 614,-423.5"/>
<path fill="none" stroke="#ff0000" stroke-width="2" d="M470,-514.5C545.24,-514.5 538.76,-425.5 614,-425.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-516.5C547.23,-514.35 540.76,-425.35 614,-427.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge16" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-537.5C534.13,-537.5 550.12,-538.5 614,-538.5"/>
<path fill="none" stroke="#0066ff" stroke-width="2" d="M470,-539.5C534,-539.5 550,-540.5 614,-540.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-541.5C533.88,-541.5 549.87,-542.5 614,-542.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge18" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-462.5C594.86,-465.31 485.48,-219.31 614,-216.5"/>
<path fill="none" stroke="#8000ff" stroke-width="2" d="M470,-464.5C596.69,-464.5 487.31,-218.5 614,-218.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-466.5C598.52,-463.69 489.14,-217.69 614,-220.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge20" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-487.5C562.43,-490.05 517.72,-334.05 614,-331.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-489.5C564.36,-489.5 519.64,-333.5 614,-333.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-491.5C566.28,-488.95 521.57,-332.95 614,-335.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge22" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-412.5C658.48,-415.51 422.07,-12.51 614,-9.5"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M470,-414.5C660.2,-414.5 423.8,-11.5 614,-11.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-416.5C661.93,-413.49 425.52,-10.49 614,-13.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge24" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-437.5C621.35,-440.42 459.1,-127.42 614,-124.5"/>
<path fill="none" stroke="#ffffff" stroke-width="2" d="M470,-439.5C623.13,-439.5 460.87,-126.5 614,-126.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-441.5C624.9,-438.58 462.65,-125.58 614,-128.5"/>
</g>
</g>
</svg>
<h1>Bill of Materials</h1><table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px"><tr><th align="left" style="border:1px solid #000000; padding: 8px">Item</th><th align="left" style="border:1px solid #000000; padding: 8px">Qty</th><th align="left" style="border:1px solid #000000; padding: 8px">Unit</th><th align="left" style="border:1px solid #000000; padding: 8px">Designators</th></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, D-Sub, male, 25 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X1</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Connector, F48, female, 48 pins</td><td align="right" style="border:1px solid #000000; padding: 4px">1</td><td style="border:1px solid #000000; padding: 4px"></td><td style="border:1px solid #000000; padding: 4px">X2</td></tr><tr><td style="border:1px solid #000000; padding: 4px">Cable, 12 x 0.25 mm² shielded</td><td align="right" style="border:1px solid #000000; padding: 4px">0.2</td><td style="border:1px solid #000000; padding: 4px">m</td><td style="border:1px solid #000000; padding: 4px">W1</td></tr></table></body></html>

BIN
examples/ex09.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

450
examples/ex09.svg Normal file
View File

@ -0,0 +1,450 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.44.0 (20200408.0750)
-->
<!-- Pages: 1 -->
<svg width="774pt" height="1159pt"
viewBox="0.00 0.00 774.00 1159.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1155)">
<polygon fill="white" stroke="transparent" points="-4,4 -4,-1155 770,-1155 770,4 -4,4"/>
<!-- X1 -->
<g id="node1" class="node">
<title>X1</title>
<polygon fill="white" stroke="black" points="0,-377 0,-722 156,-722 156,-377 0,-377"/>
<text text-anchor="middle" x="78" y="-706.8" font-family="arial" font-size="14.00">X1</text>
<polyline fill="none" stroke="black" points="0,-699 156,-699 "/>
<text text-anchor="middle" x="27.5" y="-683.8" font-family="arial" font-size="14.00">D&#45;Sub</text>
<polyline fill="none" stroke="black" points="55,-676 55,-699 "/>
<text text-anchor="middle" x="78.5" y="-683.8" font-family="arial" font-size="14.00">male</text>
<polyline fill="none" stroke="black" points="102,-676 102,-699 "/>
<text text-anchor="middle" x="129" y="-683.8" font-family="arial" font-size="14.00">25&#45;pin</text>
<polyline fill="none" stroke="black" points="0,-676 156,-676 "/>
<text text-anchor="middle" x="54.5" y="-660.8" font-family="arial" font-size="14.00">SENSE_P_1</text>
<polyline fill="none" stroke="black" points="0,-653 109,-653 "/>
<text text-anchor="middle" x="54.5" y="-637.8" font-family="arial" font-size="14.00">SENSE_N_1</text>
<polyline fill="none" stroke="black" points="0,-630 109,-630 "/>
<text text-anchor="middle" x="54.5" y="-614.8" font-family="arial" font-size="14.00">SENSE_P_2</text>
<polyline fill="none" stroke="black" points="0,-607 109,-607 "/>
<text text-anchor="middle" x="54.5" y="-591.8" font-family="arial" font-size="14.00">SENSE_N_2</text>
<polyline fill="none" stroke="black" points="0,-584 109,-584 "/>
<text text-anchor="middle" x="54.5" y="-568.8" font-family="arial" font-size="14.00">SENSE_P_3</text>
<polyline fill="none" stroke="black" points="0,-561 109,-561 "/>
<text text-anchor="middle" x="54.5" y="-545.8" font-family="arial" font-size="14.00">SENSE_N_3</text>
<polyline fill="none" stroke="black" points="0,-538 109,-538 "/>
<text text-anchor="middle" x="54.5" y="-522.8" font-family="arial" font-size="14.00">SENSE_P_4</text>
<polyline fill="none" stroke="black" points="0,-515 109,-515 "/>
<text text-anchor="middle" x="54.5" y="-499.8" font-family="arial" font-size="14.00">SENSE_N_4</text>
<polyline fill="none" stroke="black" points="0,-492 109,-492 "/>
<text text-anchor="middle" x="54.5" y="-476.8" font-family="arial" font-size="14.00">SENSE_P_5</text>
<polyline fill="none" stroke="black" points="0,-469 109,-469 "/>
<text text-anchor="middle" x="54.5" y="-453.8" font-family="arial" font-size="14.00">SENSE_N_5</text>
<polyline fill="none" stroke="black" points="0,-446 109,-446 "/>
<text text-anchor="middle" x="54.5" y="-430.8" font-family="arial" font-size="14.00">SENSE_P_6</text>
<polyline fill="none" stroke="black" points="0,-423 109,-423 "/>
<text text-anchor="middle" x="54.5" y="-407.8" font-family="arial" font-size="14.00">SENSE_N_6</text>
<polyline fill="none" stroke="black" points="0,-400 109,-400 "/>
<text text-anchor="middle" x="54.5" y="-384.8" font-family="arial" font-size="14.00">GND</text>
<polyline fill="none" stroke="black" points="109,-377 109,-676 "/>
<text text-anchor="middle" x="132.5" y="-660.8" font-family="arial" font-size="14.00">1</text>
<polyline fill="none" stroke="black" points="109,-653 156,-653 "/>
<text text-anchor="middle" x="132.5" y="-637.8" font-family="arial" font-size="14.00">14</text>
<polyline fill="none" stroke="black" points="109,-630 156,-630 "/>
<text text-anchor="middle" x="132.5" y="-614.8" font-family="arial" font-size="14.00">3</text>
<polyline fill="none" stroke="black" points="109,-607 156,-607 "/>
<text text-anchor="middle" x="132.5" y="-591.8" font-family="arial" font-size="14.00">16</text>
<polyline fill="none" stroke="black" points="109,-584 156,-584 "/>
<text text-anchor="middle" x="132.5" y="-568.8" font-family="arial" font-size="14.00">5</text>
<polyline fill="none" stroke="black" points="109,-561 156,-561 "/>
<text text-anchor="middle" x="132.5" y="-545.8" font-family="arial" font-size="14.00">18</text>
<polyline fill="none" stroke="black" points="109,-538 156,-538 "/>
<text text-anchor="middle" x="132.5" y="-522.8" font-family="arial" font-size="14.00">7</text>
<polyline fill="none" stroke="black" points="109,-515 156,-515 "/>
<text text-anchor="middle" x="132.5" y="-499.8" font-family="arial" font-size="14.00">20</text>
<polyline fill="none" stroke="black" points="109,-492 156,-492 "/>
<text text-anchor="middle" x="132.5" y="-476.8" font-family="arial" font-size="14.00">9</text>
<polyline fill="none" stroke="black" points="109,-469 156,-469 "/>
<text text-anchor="middle" x="132.5" y="-453.8" font-family="arial" font-size="14.00">22</text>
<polyline fill="none" stroke="black" points="109,-446 156,-446 "/>
<text text-anchor="middle" x="132.5" y="-430.8" font-family="arial" font-size="14.00">11</text>
<polyline fill="none" stroke="black" points="109,-423 156,-423 "/>
<text text-anchor="middle" x="132.5" y="-407.8" font-family="arial" font-size="14.00">24</text>
<polyline fill="none" stroke="black" points="109,-400 156,-400 "/>
<text text-anchor="middle" x="132.5" y="-384.8" font-family="arial" font-size="14.00">13</text>
</g>
<!-- W1 -->
<g id="node3" class="node">
<title>W1</title>
<polygon fill="none" stroke="black" points="470,-772.5 300,-772.5 300,-348.5 470,-348.5 470,-772.5"/>
<polygon fill="none" stroke="black" points="300,-749.5 300,-772.5 470,-772.5 470,-749.5 300,-749.5"/>
<text text-anchor="start" x="374" y="-757.3" font-family="arial" font-size="14.00">W1</text>
<polygon fill="none" stroke="black" points="300,-726.5 300,-749.5 330,-749.5 330,-726.5 300,-726.5"/>
<text text-anchor="start" x="304" y="-734.3" font-family="arial" font-size="14.00">12x</text>
<polygon fill="none" stroke="black" points="330,-726.5 330,-749.5 397,-749.5 397,-726.5 330,-726.5"/>
<text text-anchor="start" x="334" y="-734.3" font-family="arial" font-size="14.00">0.25 mm²</text>
<polygon fill="none" stroke="black" points="397,-726.5 397,-749.5 427,-749.5 427,-726.5 397,-726.5"/>
<text text-anchor="start" x="401" y="-734.3" font-family="arial" font-size="14.00">+ S</text>
<polygon fill="none" stroke="black" points="427,-726.5 427,-749.5 470,-749.5 470,-726.5 427,-726.5"/>
<text text-anchor="start" x="431" y="-734.3" font-family="arial" font-size="14.00">0.2 m</text>
<text text-anchor="start" x="383" y="-715.3" font-family="arial" font-size="14.00"> </text>
<text text-anchor="start" x="309" y="-698.3" font-family="arial" font-size="14.00">X1:14</text>
<text text-anchor="start" x="370" y="-698.3" font-family="arial" font-size="14.00">WH</text>
<text text-anchor="start" x="422.5" y="-698.3" font-family="arial" font-size="14.00">X2:z2</text>
<polygon fill="#ffffff" stroke="transparent" points="300,-686.5 300,-692.5 470,-692.5 470,-686.5 300,-686.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-687.5 469,-687.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-691.5 301,-691.5 "/>
<text text-anchor="start" x="313" y="-673.3" font-family="arial" font-size="14.00">X1:1</text>
<text text-anchor="start" x="372.5" y="-673.3" font-family="arial" font-size="14.00">BN</text>
<text text-anchor="start" x="422" y="-673.3" font-family="arial" font-size="14.00">X2:d4</text>
<polygon fill="#666600" stroke="transparent" stroke-width="2" points="300,-661.5 300,-667.5 470,-667.5 470,-661.5 300,-661.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-662.5 469,-662.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-666.5 301,-666.5 "/>
<text text-anchor="start" x="309" y="-648.3" font-family="arial" font-size="14.00">X1:16</text>
<text text-anchor="start" x="371" y="-648.3" font-family="arial" font-size="14.00">GN</text>
<text text-anchor="start" x="422.5" y="-648.3" font-family="arial" font-size="14.00">X2:z8</text>
<polygon fill="#00ff00" stroke="transparent" stroke-width="2" points="300,-636.5 300,-642.5 470,-642.5 470,-636.5 300,-636.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-637.5 469,-637.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-641.5 301,-641.5 "/>
<text text-anchor="start" x="313" y="-623.3" font-family="arial" font-size="14.00">X1:3</text>
<text text-anchor="start" x="372.5" y="-623.3" font-family="arial" font-size="14.00">YE</text>
<text text-anchor="start" x="418.5" y="-623.3" font-family="arial" font-size="14.00">X2:d10</text>
<polygon fill="#ffff00" stroke="transparent" stroke-width="2" points="300,-611.5 300,-617.5 470,-617.5 470,-611.5 300,-611.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-612.5 469,-612.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-616.5 301,-616.5 "/>
<text text-anchor="start" x="309" y="-598.3" font-family="arial" font-size="14.00">X1:18</text>
<text text-anchor="start" x="371.5" y="-598.3" font-family="arial" font-size="14.00">GY</text>
<text text-anchor="start" x="418.5" y="-598.3" font-family="arial" font-size="14.00">X2:z14</text>
<polygon fill="#999999" stroke="transparent" stroke-width="2" points="300,-586.5 300,-592.5 470,-592.5 470,-586.5 300,-586.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-587.5 469,-587.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-591.5 301,-591.5 "/>
<text text-anchor="start" x="313" y="-573.3" font-family="arial" font-size="14.00">X1:5</text>
<text text-anchor="start" x="372.5" y="-573.3" font-family="arial" font-size="14.00">PK</text>
<text text-anchor="start" x="418.5" y="-573.3" font-family="arial" font-size="14.00">X2:d16</text>
<polygon fill="#ff66cc" stroke="transparent" stroke-width="2" points="300,-561.5 300,-567.5 470,-567.5 470,-561.5 300,-561.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-562.5 469,-562.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-566.5 301,-566.5 "/>
<text text-anchor="start" x="309" y="-548.3" font-family="arial" font-size="14.00">X1:20</text>
<text text-anchor="start" x="372.5" y="-548.3" font-family="arial" font-size="14.00">BU</text>
<text text-anchor="start" x="418.5" y="-548.3" font-family="arial" font-size="14.00">X2:z18</text>
<polygon fill="#0066ff" stroke="transparent" stroke-width="2" points="300,-536.5 300,-542.5 470,-542.5 470,-536.5 300,-536.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-537.5 469,-537.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-541.5 301,-541.5 "/>
<text text-anchor="start" x="313" y="-523.3" font-family="arial" font-size="14.00">X1:7</text>
<text text-anchor="start" x="372" y="-523.3" font-family="arial" font-size="14.00">RD</text>
<text text-anchor="start" x="418.5" y="-523.3" font-family="arial" font-size="14.00">X2:d20</text>
<polygon fill="#ff0000" stroke="transparent" stroke-width="2" points="300,-511.5 300,-517.5 470,-517.5 470,-511.5 300,-511.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-512.5 469,-512.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-516.5 301,-516.5 "/>
<text text-anchor="start" x="309" y="-498.3" font-family="arial" font-size="14.00">X1:22</text>
<text text-anchor="start" x="372.5" y="-498.3" font-family="arial" font-size="14.00">BK</text>
<text text-anchor="start" x="418.5" y="-498.3" font-family="arial" font-size="14.00">X2:z24</text>
<polygon fill="#000000" stroke="transparent" stroke-width="2" points="300,-486.5 300,-492.5 470,-492.5 470,-486.5 300,-486.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-487.5 469,-487.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-491.5 301,-491.5 "/>
<text text-anchor="start" x="313" y="-473.3" font-family="arial" font-size="14.00">X1:9</text>
<text text-anchor="start" x="373" y="-473.3" font-family="arial" font-size="14.00">VT</text>
<text text-anchor="start" x="418.5" y="-473.3" font-family="arial" font-size="14.00">X2:d26</text>
<polygon fill="#8000ff" stroke="transparent" stroke-width="2" points="300,-461.5 300,-467.5 470,-467.5 470,-461.5 300,-461.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-462.5 469,-462.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-466.5 301,-466.5 "/>
<text text-anchor="start" x="309" y="-448.3" font-family="arial" font-size="14.00">X1:24</text>
<text text-anchor="start" x="370" y="-448.3" font-family="arial" font-size="14.00">WH</text>
<text text-anchor="start" x="418.5" y="-448.3" font-family="arial" font-size="14.00">X2:z30</text>
<polygon fill="#ffffff" stroke="transparent" stroke-width="2" points="300,-436.5 300,-442.5 470,-442.5 470,-436.5 300,-436.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-437.5 469,-437.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-441.5 301,-441.5 "/>
<text text-anchor="start" x="309.5" y="-423.3" font-family="arial" font-size="14.00">X1:11</text>
<text text-anchor="start" x="372.5" y="-423.3" font-family="arial" font-size="14.00">BN</text>
<text text-anchor="start" x="418.5" y="-423.3" font-family="arial" font-size="14.00">X2:d32</text>
<polygon fill="#666600" stroke="transparent" stroke-width="2" points="300,-411.5 300,-417.5 470,-417.5 470,-411.5 300,-411.5"/>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-412.5 469,-412.5 "/>
<polyline fill="none" stroke="black" stroke-width="2" points="469,-416.5 301,-416.5 "/>
<text text-anchor="start" x="325" y="-398.3" font-family="arial" font-size="14.00"> </text>
<text text-anchor="start" x="309" y="-379.3" font-family="arial" font-size="14.00">X1:13</text>
<text text-anchor="start" x="363" y="-379.3" font-family="arial" font-size="14.00">Shield</text>
<polyline fill="none" stroke="black" stroke-width="2" points="301,-368.5 469,-368.5 "/>
<text text-anchor="start" x="325" y="-354.3" font-family="arial" font-size="14.00"> </text>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge1" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-662.5C220,-662.5 236,-662.5 300,-662.5"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M156,-664.5C220,-664.5 236,-664.5 300,-664.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-666.5C220,-666.5 236,-666.5 300,-666.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge3" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-639.5C225.43,-641.13 234.5,-689.13 300,-687.5"/>
<path fill="none" stroke="#ffffff" stroke-width="2" d="M156,-641.5C223.46,-641.5 232.54,-689.5 300,-689.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-643.5C221.5,-641.87 230.57,-689.87 300,-691.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge5" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-616.5C219.54,-616.56 235.49,-612.56 300,-612.5"/>
<path fill="none" stroke="#ffff00" stroke-width="2" d="M156,-618.5C220.02,-618.5 235.98,-614.5 300,-614.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-620.5C220.51,-620.44 236.46,-616.44 300,-616.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge7" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-593.5C224.87,-595.05 235.03,-639.05 300,-637.5"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M156,-595.5C222.92,-595.5 233.08,-639.5 300,-639.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-597.5C220.97,-595.95 231.13,-639.95 300,-641.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge9" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-570.5C219.2,-570.72 235,-562.72 300,-562.5"/>
<path fill="none" stroke="#ff66cc" stroke-width="2" d="M156,-572.5C220.1,-572.5 235.9,-564.5 300,-564.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-574.5C221,-574.28 236.8,-566.28 300,-566.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge11" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-547.5C224.35,-548.96 235.5,-588.96 300,-587.5"/>
<path fill="none" stroke="#999999" stroke-width="2" d="M156,-549.5C222.42,-549.5 233.58,-589.5 300,-589.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-551.5C220.5,-550.04 231.65,-590.04 300,-591.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge13" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-524.5C219,-524.92 234.56,-512.92 300,-512.5"/>
<path fill="none" stroke="#ff0000" stroke-width="2" d="M156,-526.5C220.22,-526.5 235.78,-514.5 300,-514.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-528.5C221.44,-528.08 237,-516.08 300,-516.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge15" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-501.5C223.87,-502.86 235.93,-538.86 300,-537.5"/>
<path fill="none" stroke="#0066ff" stroke-width="2" d="M156,-503.5C221.97,-503.5 234.03,-539.5 300,-539.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-505.5C220.07,-504.14 232.13,-540.14 300,-541.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge17" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-478.5C218.94,-479.12 234.16,-463.12 300,-462.5"/>
<path fill="none" stroke="#8000ff" stroke-width="2" d="M156,-480.5C220.39,-480.5 235.61,-464.5 300,-464.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-482.5C221.84,-481.88 237.06,-465.88 300,-466.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge19" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-455.5C223.42,-456.75 236.29,-488.75 300,-487.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-457.5C221.56,-457.5 234.44,-489.5 300,-489.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-459.5C219.71,-458.25 232.58,-490.25 300,-491.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge21" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-432.5C219.01,-433.31 233.78,-413.31 300,-412.5"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M156,-434.5C220.61,-434.5 235.39,-414.5 300,-414.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-436.5C222.22,-435.69 236.99,-415.69 300,-416.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge23" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-409.5C223,-410.63 236.6,-438.63 300,-437.5"/>
<path fill="none" stroke="#ffffff" stroke-width="2" d="M156,-411.5C221.2,-411.5 234.8,-439.5 300,-439.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-413.5C219.4,-412.37 233,-440.37 300,-441.5"/>
</g>
<!-- X1&#45;&#45;W1 -->
<g id="edge25" class="edge">
<title>X1:e&#45;&#45;W1:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M156,-388.5C220.5,-388.5 235.5,-370.5 300,-370.5"/>
</g>
<!-- X2 -->
<g id="node2" class="node">
<title>X2</title>
<polygon fill="white" stroke="black" points="614,-0.5 614,-1150.5 766,-1150.5 766,-0.5 614,-0.5"/>
<text text-anchor="middle" x="690" y="-1135.3" font-family="arial" font-size="14.00">X2</text>
<polyline fill="none" stroke="black" points="614,-1127.5 766,-1127.5 "/>
<text text-anchor="middle" x="634" y="-1112.3" font-family="arial" font-size="14.00">F48</text>
<polyline fill="none" stroke="black" points="654,-1104.5 654,-1127.5 "/>
<text text-anchor="middle" x="683" y="-1112.3" font-family="arial" font-size="14.00">female</text>
<polyline fill="none" stroke="black" points="712,-1104.5 712,-1127.5 "/>
<text text-anchor="middle" x="739" y="-1112.3" font-family="arial" font-size="14.00">48&#45;pin</text>
<polyline fill="none" stroke="black" points="614,-1104.5 766,-1104.5 "/>
<text text-anchor="middle" x="690" y="-1089.3" font-family="arial" font-size="14.00">z2</text>
<polyline fill="none" stroke="black" points="614,-1081.5 766,-1081.5 "/>
<text text-anchor="middle" x="690" y="-1066.3" font-family="arial" font-size="14.00">b2</text>
<polyline fill="none" stroke="black" points="614,-1058.5 766,-1058.5 "/>
<text text-anchor="middle" x="690" y="-1043.3" font-family="arial" font-size="14.00">d2</text>
<polyline fill="none" stroke="black" points="614,-1035.5 766,-1035.5 "/>
<text text-anchor="middle" x="690" y="-1020.3" font-family="arial" font-size="14.00">z4</text>
<polyline fill="none" stroke="black" points="614,-1012.5 766,-1012.5 "/>
<text text-anchor="middle" x="690" y="-997.3" font-family="arial" font-size="14.00">b4</text>
<polyline fill="none" stroke="black" points="614,-989.5 766,-989.5 "/>
<text text-anchor="middle" x="690" y="-974.3" font-family="arial" font-size="14.00">d4</text>
<polyline fill="none" stroke="black" points="614,-966.5 766,-966.5 "/>
<text text-anchor="middle" x="690" y="-951.3" font-family="arial" font-size="14.00">z6</text>
<polyline fill="none" stroke="black" points="614,-943.5 766,-943.5 "/>
<text text-anchor="middle" x="690" y="-928.3" font-family="arial" font-size="14.00">b6</text>
<polyline fill="none" stroke="black" points="614,-920.5 766,-920.5 "/>
<text text-anchor="middle" x="690" y="-905.3" font-family="arial" font-size="14.00">d6</text>
<polyline fill="none" stroke="black" points="614,-897.5 766,-897.5 "/>
<text text-anchor="middle" x="690" y="-882.3" font-family="arial" font-size="14.00">z8</text>
<polyline fill="none" stroke="black" points="614,-874.5 766,-874.5 "/>
<text text-anchor="middle" x="690" y="-859.3" font-family="arial" font-size="14.00">b8</text>
<polyline fill="none" stroke="black" points="614,-851.5 766,-851.5 "/>
<text text-anchor="middle" x="690" y="-836.3" font-family="arial" font-size="14.00">d8</text>
<polyline fill="none" stroke="black" points="614,-828.5 766,-828.5 "/>
<text text-anchor="middle" x="690" y="-813.3" font-family="arial" font-size="14.00">z10</text>
<polyline fill="none" stroke="black" points="614,-805.5 766,-805.5 "/>
<text text-anchor="middle" x="690" y="-790.3" font-family="arial" font-size="14.00">b10</text>
<polyline fill="none" stroke="black" points="614,-782.5 766,-782.5 "/>
<text text-anchor="middle" x="690" y="-767.3" font-family="arial" font-size="14.00">d10</text>
<polyline fill="none" stroke="black" points="614,-759.5 766,-759.5 "/>
<text text-anchor="middle" x="690" y="-744.3" font-family="arial" font-size="14.00">z12</text>
<polyline fill="none" stroke="black" points="614,-736.5 766,-736.5 "/>
<text text-anchor="middle" x="690" y="-721.3" font-family="arial" font-size="14.00">b12</text>
<polyline fill="none" stroke="black" points="614,-713.5 766,-713.5 "/>
<text text-anchor="middle" x="690" y="-698.3" font-family="arial" font-size="14.00">d12</text>
<polyline fill="none" stroke="black" points="614,-690.5 766,-690.5 "/>
<text text-anchor="middle" x="690" y="-675.3" font-family="arial" font-size="14.00">z14</text>
<polyline fill="none" stroke="black" points="614,-667.5 766,-667.5 "/>
<text text-anchor="middle" x="690" y="-652.3" font-family="arial" font-size="14.00">b14</text>
<polyline fill="none" stroke="black" points="614,-644.5 766,-644.5 "/>
<text text-anchor="middle" x="690" y="-629.3" font-family="arial" font-size="14.00">d14</text>
<polyline fill="none" stroke="black" points="614,-621.5 766,-621.5 "/>
<text text-anchor="middle" x="690" y="-606.3" font-family="arial" font-size="14.00">z16</text>
<polyline fill="none" stroke="black" points="614,-598.5 766,-598.5 "/>
<text text-anchor="middle" x="690" y="-583.3" font-family="arial" font-size="14.00">b16</text>
<polyline fill="none" stroke="black" points="614,-575.5 766,-575.5 "/>
<text text-anchor="middle" x="690" y="-560.3" font-family="arial" font-size="14.00">d16</text>
<polyline fill="none" stroke="black" points="614,-552.5 766,-552.5 "/>
<text text-anchor="middle" x="690" y="-537.3" font-family="arial" font-size="14.00">z18</text>
<polyline fill="none" stroke="black" points="614,-529.5 766,-529.5 "/>
<text text-anchor="middle" x="690" y="-514.3" font-family="arial" font-size="14.00">b18</text>
<polyline fill="none" stroke="black" points="614,-506.5 766,-506.5 "/>
<text text-anchor="middle" x="690" y="-491.3" font-family="arial" font-size="14.00">d18</text>
<polyline fill="none" stroke="black" points="614,-483.5 766,-483.5 "/>
<text text-anchor="middle" x="690" y="-468.3" font-family="arial" font-size="14.00">z20</text>
<polyline fill="none" stroke="black" points="614,-460.5 766,-460.5 "/>
<text text-anchor="middle" x="690" y="-445.3" font-family="arial" font-size="14.00">b20</text>
<polyline fill="none" stroke="black" points="614,-437.5 766,-437.5 "/>
<text text-anchor="middle" x="690" y="-422.3" font-family="arial" font-size="14.00">d20</text>
<polyline fill="none" stroke="black" points="614,-414.5 766,-414.5 "/>
<text text-anchor="middle" x="690" y="-399.3" font-family="arial" font-size="14.00">z22</text>
<polyline fill="none" stroke="black" points="614,-391.5 766,-391.5 "/>
<text text-anchor="middle" x="690" y="-376.3" font-family="arial" font-size="14.00">b22</text>
<polyline fill="none" stroke="black" points="614,-368.5 766,-368.5 "/>
<text text-anchor="middle" x="690" y="-353.3" font-family="arial" font-size="14.00">d22</text>
<polyline fill="none" stroke="black" points="614,-345.5 766,-345.5 "/>
<text text-anchor="middle" x="690" y="-330.3" font-family="arial" font-size="14.00">z24</text>
<polyline fill="none" stroke="black" points="614,-322.5 766,-322.5 "/>
<text text-anchor="middle" x="690" y="-307.3" font-family="arial" font-size="14.00">b24</text>
<polyline fill="none" stroke="black" points="614,-299.5 766,-299.5 "/>
<text text-anchor="middle" x="690" y="-284.3" font-family="arial" font-size="14.00">d24</text>
<polyline fill="none" stroke="black" points="614,-276.5 766,-276.5 "/>
<text text-anchor="middle" x="690" y="-261.3" font-family="arial" font-size="14.00">z26</text>
<polyline fill="none" stroke="black" points="614,-253.5 766,-253.5 "/>
<text text-anchor="middle" x="690" y="-238.3" font-family="arial" font-size="14.00">b26</text>
<polyline fill="none" stroke="black" points="614,-230.5 766,-230.5 "/>
<text text-anchor="middle" x="690" y="-215.3" font-family="arial" font-size="14.00">d26</text>
<polyline fill="none" stroke="black" points="614,-207.5 766,-207.5 "/>
<text text-anchor="middle" x="690" y="-192.3" font-family="arial" font-size="14.00">z28</text>
<polyline fill="none" stroke="black" points="614,-184.5 766,-184.5 "/>
<text text-anchor="middle" x="690" y="-169.3" font-family="arial" font-size="14.00">b28</text>
<polyline fill="none" stroke="black" points="614,-161.5 766,-161.5 "/>
<text text-anchor="middle" x="690" y="-146.3" font-family="arial" font-size="14.00">d28</text>
<polyline fill="none" stroke="black" points="614,-138.5 766,-138.5 "/>
<text text-anchor="middle" x="690" y="-123.3" font-family="arial" font-size="14.00">z30</text>
<polyline fill="none" stroke="black" points="614,-115.5 766,-115.5 "/>
<text text-anchor="middle" x="690" y="-100.3" font-family="arial" font-size="14.00">b30</text>
<polyline fill="none" stroke="black" points="614,-92.5 766,-92.5 "/>
<text text-anchor="middle" x="690" y="-77.3" font-family="arial" font-size="14.00">d30</text>
<polyline fill="none" stroke="black" points="614,-69.5 766,-69.5 "/>
<text text-anchor="middle" x="690" y="-54.3" font-family="arial" font-size="14.00">z32</text>
<polyline fill="none" stroke="black" points="614,-46.5 766,-46.5 "/>
<text text-anchor="middle" x="690" y="-31.3" font-family="arial" font-size="14.00">b32</text>
<polyline fill="none" stroke="black" points="614,-23.5 766,-23.5 "/>
<text text-anchor="middle" x="690" y="-8.3" font-family="arial" font-size="14.00">d32</text>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge2" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-662.5C625.31,-665.42 462.24,-979.42 614,-976.5"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M470,-664.5C623.53,-664.5 460.47,-978.5 614,-978.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-666.5C621.76,-663.58 458.69,-977.58 614,-980.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge4" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-687.5C662.35,-690.51 425.1,-1094.51 614,-1091.5"/>
<path fill="none" stroke="#ffffff" stroke-width="2" d="M470,-689.5C660.62,-689.5 423.38,-1093.5 614,-1093.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-691.5C658.9,-688.49 421.65,-1092.49 614,-1095.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge6" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-612.5C566.6,-615.06 521.24,-772.06 614,-769.5"/>
<path fill="none" stroke="#ffff00" stroke-width="2" d="M470,-614.5C564.68,-614.5 519.32,-771.5 614,-771.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-616.5C562.76,-613.94 517.4,-770.94 614,-773.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge8" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-637.5C598.9,-640.31 488.76,-887.31 614,-884.5"/>
<path fill="none" stroke="#00ff00" stroke-width="2" d="M470,-639.5C597.07,-639.5 486.93,-886.5 614,-886.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-641.5C595.24,-638.69 485.1,-885.69 614,-888.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge10" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-562.5C533.88,-562.5 549.87,-561.5 614,-561.5"/>
<path fill="none" stroke="#ff66cc" stroke-width="2" d="M470,-564.5C534,-564.5 550,-563.5 614,-563.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-566.5C534.13,-566.5 550.12,-565.5 614,-565.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge12" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-587.5C547.47,-589.65 540.52,-679.65 614,-677.5"/>
<path fill="none" stroke="#999999" stroke-width="2" d="M470,-589.5C545.47,-589.5 538.53,-679.5 614,-679.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-591.5C543.48,-589.35 536.53,-679.35 614,-681.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge14" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-512.5C543.24,-514.65 536.77,-425.65 614,-423.5"/>
<path fill="none" stroke="#ff0000" stroke-width="2" d="M470,-514.5C545.24,-514.5 538.76,-425.5 614,-425.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-516.5C547.23,-514.35 540.76,-425.35 614,-427.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge16" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-537.5C534.13,-537.5 550.12,-538.5 614,-538.5"/>
<path fill="none" stroke="#0066ff" stroke-width="2" d="M470,-539.5C534,-539.5 550,-540.5 614,-540.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-541.5C533.88,-541.5 549.87,-542.5 614,-542.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge18" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-462.5C594.86,-465.31 485.48,-219.31 614,-216.5"/>
<path fill="none" stroke="#8000ff" stroke-width="2" d="M470,-464.5C596.69,-464.5 487.31,-218.5 614,-218.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-466.5C598.52,-463.69 489.14,-217.69 614,-220.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge20" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-487.5C562.43,-490.05 517.72,-334.05 614,-331.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-489.5C564.36,-489.5 519.64,-333.5 614,-333.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-491.5C566.28,-488.95 521.57,-332.95 614,-335.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge22" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-412.5C658.48,-415.51 422.07,-12.51 614,-9.5"/>
<path fill="none" stroke="#666600" stroke-width="2" d="M470,-414.5C660.2,-414.5 423.8,-11.5 614,-11.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-416.5C661.93,-413.49 425.52,-10.49 614,-13.5"/>
</g>
<!-- W1&#45;&#45;X2 -->
<g id="edge24" class="edge">
<title>W1:e&#45;&#45;X2:w</title>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-437.5C621.35,-440.42 459.1,-127.42 614,-124.5"/>
<path fill="none" stroke="#ffffff" stroke-width="2" d="M470,-439.5C623.13,-439.5 460.87,-126.5 614,-126.5"/>
<path fill="none" stroke="#000000" stroke-width="2" d="M470,-441.5C624.9,-438.58 462.65,-125.58 614,-128.5"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 33 KiB

31
examples/ex09.yml Normal file
View File

@ -0,0 +1,31 @@
# contributed by @kimmoli
connectors:
X1:
type: D-Sub
subtype: male
pincount: 25
pinnumbers: [1,14,3,16,5,18,7,20,9,22,11,24,13]
pinout: [ SENSE_P_1, SENSE_N_1, SENSE_P_2, SENSE_N_2, SENSE_P_3, SENSE_N_3, SENSE_P_4,SENSE_N_4, SENSE_P_5, SENSE_N_5, SENSE_P_6, SENSE_N_6, GND ]
X2:
type: F48
subtype: female
pincount: 48
pinnumbers: [ z2,b2,d2,z4,b4,d4,z6,b6,d6,z8,b8,d8,z10,b10,d10,z12,b12,d12,z14,b14,d14,z16,b16,d16,z18,b18,d18,z20,b20,d20,z22,b22,d22,z24,b24,d24,z26,b26,d26,z28,b28,d28,z30,b30,d30,z32,b32,d32 ]
cables:
W1:
gauge: 0.25 mm2
length: 0.2
color_code: DIN
wirecount: 12
shield: true
connections:
-
- X1: [1,14,3,16,5,18,7,20,9,22,11,24]
- W1: [2,1,4,3,6,5,8,7,10,9,12,11]
- X2: [d4,z2,d10,z8,d16,z14,d20,z18,d26,z24,d32,z30]
-
- X1: 13
- W1: s

View File

@ -39,3 +39,21 @@
[Source](ex07.yml) - [Bill of Materials](ex07.bom.tsv)
## Example 07
![](ex07.png)
[Source](ex07.yml) - [Bill of Materials](ex07.bom.tsv)
## Example 08
![](ex08.png)
[Source](ex08.yml) - [Bill of Materials](ex08.bom.tsv)
## Example 09
![](ex09.png)
[Source](ex09.yml) - [Bill of Materials](ex09.bom.tsv)

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
.

45
setup.py Normal file
View File

@ -0,0 +1,45 @@
#!/usr/bin/python3
import os
from setuptools import setup, find_packages
project_name = 'wireviz'
# Utility function to read the README file.
# Used for the long_description. It's nice, because now 1) we have a top level
# README file and 2) it's easier to type in the README file than to put a raw
# string in below ...
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
setup(
name=project_name,
version='0.1',
author='Daniel Rojas',
#author_email='',
description='Easily document cables and wiring harnesses',
long_description=read(os.path.join(os.path.dirname(__file__), 'README.md')),
long_description_content_type='text/markdown',
install_requires=[
'pyyaml',
'graphviz',
],
license='GPLv3',
keywords='cable connector hardware harness wiring wiring-diagram wiring-harness',
url='https://github.com/formatc1702/WireViz',
package_dir={'': 'src'},
packages=find_packages('src'),
entry_points={
'console_scripts': ['wireviz=wireviz.wireviz:main'],
},
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Console',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Topic :: Utilities',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
],
)

View File

@ -1,63 +0,0 @@
# graphviz - create dot, save, render, view
"""Assemble DOT source code and render it with Graphviz.
>>> dot = Digraph(comment='The Round Table')
>>> dot.node('A', 'King Arthur')
>>> dot.node('B', 'Sir Bedevere the Wise')
>>> dot.node('L', 'Sir Lancelot the Brave')
>>> dot.edges(['AB', 'AL'])
>>> dot.edge('B', 'L', constraint='false')
>>> print(dot) #doctest: +NORMALIZE_WHITESPACE
// The Round Table
digraph {
A [label="King Arthur"]
B [label="Sir Bedevere the Wise"]
L [label="Sir Lancelot the Brave"]
A -> B
A -> L
B -> L [constraint=false]
}
"""
from .dot import Graph, Digraph
from .files import Source
from .lang import escape, nohtml
from .backend import (render, pipe, version, view,
ENGINES, FORMATS, RENDERERS, FORMATTERS,
ExecutableNotFound, RequiredArgumentError)
__all__ = [
'Graph', 'Digraph',
'Source',
'escape', 'nohtml',
'render', 'pipe', 'version', 'view',
'ENGINES', 'FORMATS', 'RENDERERS', 'FORMATTERS',
'ExecutableNotFound', 'RequiredArgumentError',
]
__title__ = 'graphviz'
__version__ = '0.14.1.dev0'
__author__ = 'Sebastian Bank <sebastian.bank@uni-leipzig.de>'
__license__ = 'MIT, see LICENSE.txt'
__copyright__ = 'Copyright (c) 2013-2020 Sebastian Bank'
#: Set of known layout commands used for rendering (``'dot'``, ``'neato'``, ...)
ENGINES = ENGINES
#: Set of known output formats for rendering (``'pdf'``, ``'png'``, ...)
FORMATS = FORMATS
#: Set of known output formatters for rendering (``'cairo'``, ``'gd'``, ...)
FORMATTERS = FORMATTERS
#: Set of known output renderers for rendering (``'cairo'``, ``'gd'``, ...)
RENDERERS = RENDERERS
ExecutableNotFound = ExecutableNotFound
RequiredArgumentError = RequiredArgumentError

View File

@ -1,69 +0,0 @@
# _compat.py - Python 2/3 compatibility
import os
import sys
import operator
import subprocess
PY2 = (sys.version_info.major == 2)
if PY2:
string_classes = (str, unicode) # needed individually for sublassing
text_type = unicode
iteritems = operator.methodcaller('iteritems')
def makedirs(name, mode=0o777, exist_ok=False):
try:
os.makedirs(name, mode)
except OSError:
if not exist_ok or not os.path.isdir(name):
raise
def stderr_write_bytes(data, flush=False):
"""Write data str to sys.stderr (flush if requested)."""
sys.stderr.write(data)
if flush:
sys.stderr.flush()
def Popen_stderr_devnull(*args, **kwargs): # noqa: N802
with open(os.devnull, 'w') as f:
return subprocess.Popen(*args, stderr=f, **kwargs)
class CalledProcessError(subprocess.CalledProcessError):
def __init__(self, returncode, cmd, output=None, stderr=None):
super(CalledProcessError, self).__init__(returncode, cmd, output)
self.stderr = stderr
@property # pragma: no cover
def stdout(self):
return self.output
@stdout.setter # pragma: no cover
def stdout(self, value):
self.output = value
else:
string_classes = (str,)
text_type = str
def iteritems(d):
return iter(d.items())
def makedirs(name, mode=0o777, exist_ok=False): # allow os.makedirs mocking
return os.makedirs(name, mode, exist_ok=exist_ok)
def stderr_write_bytes(data, flush=False):
"""Encode data str and write to sys.stderr (flush if requested)."""
encoding = sys.stderr.encoding or sys.getdefaultencoding()
sys.stderr.write(data.decode(encoding))
if flush:
sys.stderr.flush()
def Popen_stderr_devnull(*args, **kwargs): # noqa: N802
return subprocess.Popen(*args, stderr=subprocess.DEVNULL, **kwargs)
CalledProcessError = subprocess.CalledProcessError

View File

@ -1,312 +0,0 @@
# backend.py - execute rendering, open files in viewer
import os
import re
import errno
import logging
import platform
import subprocess
from . import _compat
from . import tools
__all__ = [
'render', 'pipe', 'version', 'view',
'ENGINES', 'FORMATS', 'RENDERERS', 'FORMATTERS',
'ExecutableNotFound', 'RequiredArgumentError',
]
ENGINES = { # http://www.graphviz.org/pdf/dot.1.pdf
'dot', 'neato', 'twopi', 'circo', 'fdp', 'sfdp', 'patchwork', 'osage',
}
FORMATS = { # http://www.graphviz.org/doc/info/output.html
'bmp',
'canon', 'dot', 'gv', 'xdot', 'xdot1.2', 'xdot1.4',
'cgimage',
'cmap',
'eps',
'exr',
'fig',
'gd', 'gd2',
'gif',
'gtk',
'ico',
'imap', 'cmapx',
'imap_np', 'cmapx_np',
'ismap',
'jp2',
'jpg', 'jpeg', 'jpe',
'json', 'json0', 'dot_json', 'xdot_json', # Graphviz 2.40
'pct', 'pict',
'pdf',
'pic',
'plain', 'plain-ext',
'png',
'pov',
'ps',
'ps2',
'psd',
'sgi',
'svg', 'svgz',
'tga',
'tif', 'tiff',
'tk',
'vml', 'vmlz',
'vrml',
'wbmp',
'webp',
'xlib',
'x11',
}
RENDERERS = { # $ dot -T:
'cairo',
'dot',
'fig',
'gd',
'gdiplus',
'map',
'pic',
'pov',
'ps',
'svg',
'tk',
'vml',
'vrml',
'xdot',
}
FORMATTERS = {'cairo', 'core', 'gd', 'gdiplus', 'gdwbmp', 'xlib'}
PLATFORM = platform.system().lower()
log = logging.getLogger(__name__)
class ExecutableNotFound(RuntimeError):
"""Exception raised if the Graphviz executable is not found."""
_msg = ('failed to execute %r, '
'make sure the Graphviz executables are on your systems\' PATH')
def __init__(self, args):
super(ExecutableNotFound, self).__init__(self._msg % args)
class RequiredArgumentError(Exception):
"""Exception raised if a required argument is missing."""
class CalledProcessError(_compat.CalledProcessError):
def __str__(self):
s = super(CalledProcessError, self).__str__()
return '%s [stderr: %r]' % (s, self.stderr)
def command(engine, format_, filepath=None, renderer=None, formatter=None):
"""Return args list for ``subprocess.Popen`` and name of the rendered file."""
if formatter is not None and renderer is None:
raise RequiredArgumentError('formatter given without renderer')
if engine not in ENGINES:
raise ValueError('unknown engine: %r' % engine)
if format_ not in FORMATS:
raise ValueError('unknown format: %r' % format_)
if renderer is not None and renderer not in RENDERERS:
raise ValueError('unknown renderer: %r' % renderer)
if formatter is not None and formatter not in FORMATTERS:
raise ValueError('unknown formatter: %r' % formatter)
output_format = [f for f in (format_, renderer, formatter) if f is not None]
cmd = [engine, '-T%s' % ':'.join(output_format)]
if filepath is None:
rendered = None
else:
cmd.extend(['-O', filepath])
suffix = '.'.join(reversed(output_format))
rendered = '%s.%s' % (filepath, suffix)
return cmd, rendered
if PLATFORM == 'windows': # pragma: no cover
def get_startupinfo():
"""Return subprocess.STARTUPINFO instance hiding the console window."""
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
return startupinfo
else:
def get_startupinfo():
"""Return None for startupinfo argument of ``subprocess.Popen``."""
return None
def run(cmd, input=None, capture_output=False, check=False, encoding=None,
quiet=False, **kwargs):
"""Run the command described by cmd and return its (stdout, stderr) tuple."""
log.debug('run %r', cmd)
if input is not None:
kwargs['stdin'] = subprocess.PIPE
if encoding is not None:
input = input.encode(encoding)
if capture_output:
kwargs['stdout'] = kwargs['stderr'] = subprocess.PIPE
try:
proc = subprocess.Popen(cmd, startupinfo=get_startupinfo(), **kwargs)
except OSError as e:
if e.errno == errno.ENOENT:
raise ExecutableNotFound(cmd)
else:
raise
out, err = proc.communicate(input)
if not quiet and err:
_compat.stderr_write_bytes(err, flush=True)
if encoding is not None:
if out is not None:
out = out.decode(encoding)
if err is not None:
err = err.decode(encoding)
if check and proc.returncode:
raise CalledProcessError(proc.returncode, cmd,
output=out, stderr=err)
return out, err
def render(engine, format, filepath, renderer=None, formatter=None, quiet=False):
"""Render file with Graphviz ``engine`` into ``format``, return result filename.
Args:
engine: The layout commmand used for rendering (``'dot'``, ``'neato'``, ...).
format: The output format used for rendering (``'pdf'``, ``'png'``, ...).
filepath: Path to the DOT source file to render.
renderer: The output renderer used for rendering (``'cairo'``, ``'gd'``, ...).
formatter: The output formatter used for rendering (``'cairo'``, ``'gd'``, ...).
quiet (bool): Suppress ``stderr`` output from the layout subprocess.
Returns:
The (possibly relative) path of the rendered file.
Raises:
ValueError: If ``engine``, ``format``, ``renderer``, or ``formatter`` are not known.
graphviz.RequiredArgumentError: If ``formatter`` is given but ``renderer`` is None.
graphviz.ExecutableNotFound: If the Graphviz executable is not found.
subprocess.CalledProcessError: If the exit status is non-zero.
The layout command is started from the directory of ``filepath``, so that
references to external files (e.g. ``[image=...]``) can be given as paths
relative to the DOT source file.
"""
dirname, filename = os.path.split(filepath)
del filepath
cmd, rendered = command(engine, format, filename, renderer, formatter)
if dirname:
cwd = dirname
rendered = os.path.join(dirname, rendered)
else:
cwd = None
run(cmd, capture_output=True, cwd=cwd, check=True, quiet=quiet)
return rendered
def pipe(engine, format, data, renderer=None, formatter=None, quiet=False):
"""Return ``data`` piped through Graphviz ``engine`` into ``format``.
Args:
engine: The layout commmand used for rendering (``'dot'``, ``'neato'``, ...).
format: The output format used for rendering (``'pdf'``, ``'png'``, ...).
data: The binary (encoded) DOT source string to render.
renderer: The output renderer used for rendering (``'cairo'``, ``'gd'``, ...).
formatter: The output formatter used for rendering (``'cairo'``, ``'gd'``, ...).
quiet (bool): Suppress ``stderr`` output from the layout subprocess.
Returns:
Binary (encoded) stdout of the layout command.
Raises:
ValueError: If ``engine``, ``format``, ``renderer``, or ``formatter`` are not known.
graphviz.RequiredArgumentError: If ``formatter`` is given but ``renderer`` is None.
graphviz.ExecutableNotFound: If the Graphviz executable is not found.
subprocess.CalledProcessError: If the exit status is non-zero.
"""
cmd, _ = command(engine, format, None, renderer, formatter)
out, _ = run(cmd, input=data, capture_output=True, check=True, quiet=quiet)
return out
def version():
"""Return the version number tuple from the ``stderr`` output of ``dot -V``.
Returns:
Two, three, or four ``int`` version ``tuple``.
Raises:
graphviz.ExecutableNotFound: If the Graphviz executable is not found.
subprocess.CalledProcessError: If the exit status is non-zero.
RuntimmeError: If the output cannot be parsed into a version number.
"""
cmd = ['dot', '-V']
out, _ = run(cmd, check=True, encoding='ascii',
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
ma = re.search(r'graphviz version (\d+\.\d+(?:\.\d+){,2}) ', out)
if ma is None:
raise RuntimeError('cannot parse %r output: %r' % (cmd, out))
return tuple(int(d) for d in ma.group(1).split('.'))
def view(filepath, quiet=False):
"""Open filepath with its default viewing application (platform-specific).
Args:
filepath: Path to the file to open in viewer.
quiet (bool): Suppress ``stderr`` output from the viewer process
(ineffective on Windows).
Raises:
RuntimeError: If the current platform is not supported.
"""
try:
view_func = getattr(view, PLATFORM)
except AttributeError:
raise RuntimeError('platform %r not supported' % PLATFORM)
view_func(filepath, quiet)
@tools.attach(view, 'darwin')
def view_darwin(filepath, quiet):
"""Open filepath with its default application (mac)."""
cmd = ['open', filepath]
log.debug('view: %r', cmd)
popen_func = _compat.Popen_stderr_devnull if quiet else subprocess.Popen
popen_func(cmd)
@tools.attach(view, 'linux')
@tools.attach(view, 'freebsd')
def view_unixoid(filepath, quiet):
"""Open filepath in the user's preferred application (linux, freebsd)."""
cmd = ['xdg-open', filepath]
log.debug('view: %r', cmd)
popen_func = _compat.Popen_stderr_devnull if quiet else subprocess.Popen
popen_func(cmd)
@tools.attach(view, 'windows')
def view_windows(filepath, quiet):
"""Start filepath with its associated application (windows)."""
# TODO: implement quiet=True
filepath = os.path.normpath(filepath)
log.debug('view: %r', filepath)
os.startfile(filepath)

View File

@ -1,287 +0,0 @@
# dot.py - create dot code
r"""Assemble DOT source code objects.
>>> dot = Graph(comment=u'M\xf8nti Pyth\xf8n ik den H\xf8lie Grailen')
>>> dot.node(u'M\xf8\xf8se')
>>> dot.node('trained_by', u'trained by')
>>> dot.node('tutte', u'TUTTE HERMSGERVORDENBROTBORDA')
>>> dot.edge(u'M\xf8\xf8se', 'trained_by')
>>> dot.edge('trained_by', 'tutte')
>>> dot.node_attr['shape'] = 'rectangle'
>>> print(dot.source.replace(u'\xf8', '0')) #doctest: +NORMALIZE_WHITESPACE
// M0nti Pyth0n ik den H0lie Grailen
graph {
node [shape=rectangle]
"M00se"
trained_by [label="trained by"]
tutte [label="TUTTE HERMSGERVORDENBROTBORDA"]
"M00se" -- trained_by
trained_by -- tutte
}
>>> dot.view('test-output/m00se.gv') # doctest: +SKIP
'test-output/m00se.gv.pdf'
"""
from . import lang
from . import files
__all__ = ['Graph', 'Digraph']
class Dot(files.File):
"""Assemble, save, and render DOT source code, open result in viewer."""
_comment = '// %s'
_subgraph = 'subgraph %s{'
_subgraph_plain = '%s{'
_node = _attr = '\t%s%s'
_attr_plain = _attr % ('%s', '')
_tail = '}'
_quote = staticmethod(lang.quote)
_quote_edge = staticmethod(lang.quote_edge)
_a_list = staticmethod(lang.a_list)
_attr_list = staticmethod(lang.attr_list)
def __init__(self, name=None, comment=None,
filename=None, directory=None,
format=None, engine=None, encoding=files.ENCODING,
graph_attr=None, node_attr=None, edge_attr=None, body=None,
strict=False):
self.name = name
self.comment = comment
super(Dot, self).__init__(filename, directory, format, engine, encoding)
self.graph_attr = dict(graph_attr) if graph_attr is not None else {}
self.node_attr = dict(node_attr) if node_attr is not None else {}
self.edge_attr = dict(edge_attr) if edge_attr is not None else {}
self.body = list(body) if body is not None else []
self.strict = strict
def _kwargs(self):
result = super(Dot, self)._kwargs()
result.update(name=self.name,
comment=self.comment,
graph_attr=dict(self.graph_attr),
node_attr=dict(self.node_attr),
edge_attr=dict(self.edge_attr),
body=list(self.body),
strict=self.strict)
return result
def clear(self, keep_attrs=False):
"""Reset content to an empty body, clear graph/node/egde_attr mappings.
Args:
keep_attrs (bool): preserve graph/node/egde_attr mappings
"""
if not keep_attrs:
for a in (self.graph_attr, self.node_attr, self.edge_attr):
a.clear()
del self.body[:]
def __iter__(self, subgraph=False):
"""Yield the DOT source code line by line (as graph or subgraph)."""
if self.comment:
yield self._comment % self.comment
if subgraph:
if self.strict:
raise ValueError('subgraphs cannot be strict')
head = self._subgraph if self.name else self._subgraph_plain
else:
head = self._head_strict if self.strict else self._head
yield head % (self._quote(self.name) + ' ' if self.name else '')
for kw in ('graph', 'node', 'edge'):
attrs = getattr(self, '%s_attr' % kw)
if attrs:
yield self._attr % (kw, self._attr_list(None, attrs))
for line in self.body:
yield line
yield self._tail
def __str__(self):
"""The DOT source code as string."""
return '\n'.join(self)
source = property(__str__, doc=__str__.__doc__)
def node(self, name, label=None, _attributes=None, **attrs):
"""Create a node.
Args:
name: Unique identifier for the node inside the source.
label: Caption to be displayed (defaults to the node ``name``).
attrs: Any additional node attributes (must be strings).
"""
name = self._quote(name)
attr_list = self._attr_list(label, attrs, _attributes)
line = self._node % (name, attr_list)
self.body.append(line)
def edge(self, tail_name, head_name, label=None, _attributes=None, **attrs):
"""Create an edge between two nodes.
Args:
tail_name: Start node identifier.
head_name: End node identifier.
label: Caption to be displayed near the edge.
attrs: Any additional edge attributes (must be strings).
"""
tail_name = self._quote_edge(tail_name)
head_name = self._quote_edge(head_name)
attr_list = self._attr_list(label, attrs, _attributes)
line = self._edge % (tail_name, head_name, attr_list)
self.body.append(line)
def edges(self, tail_head_iter):
"""Create a bunch of edges.
Args:
tail_head_iter: Iterable of ``(tail_name, head_name)`` pairs.
"""
edge = self._edge_plain
quote = self._quote_edge
lines = (edge % (quote(t), quote(h)) for t, h in tail_head_iter)
self.body.extend(lines)
def attr(self, kw=None, _attributes=None, **attrs):
"""Add a general or graph/node/edge attribute statement.
Args:
kw: Attributes target (``None`` or ``'graph'``, ``'node'``, ``'edge'``).
attrs: Attributes to be set (must be strings, may be empty).
See the :ref:`usage examples in the User Guide <attributes>`.
"""
if kw is not None and kw.lower() not in ('graph', 'node', 'edge'):
raise ValueError('attr statement must target graph, node, or edge: '
'%r' % kw)
if attrs or _attributes:
if kw is None:
a_list = self._a_list(None, attrs, _attributes)
line = self._attr_plain % a_list
else:
attr_list = self._attr_list(None, attrs, _attributes)
line = self._attr % (kw, attr_list)
self.body.append(line)
def subgraph(self, graph=None, name=None, comment=None,
graph_attr=None, node_attr=None, edge_attr=None, body=None):
"""Add the current content of the given sole ``graph`` argument as subgraph \
or return a context manager returning a new graph instance created \
with the given (``name``, ``comment``, etc.) arguments whose content is \
added as subgraph when leaving the context manager's ``with``-block.
Args:
graph: An instance of the same kind (:class:`.Graph`, :class:`.Digraph`)
as the current graph (sole argument in non-with-block use).
name: Subgraph name (``with``-block use).
comment: Subgraph comment (``with``-block use).
graph_attr: Subgraph-level attribute-value mapping (``with``-block use).
node_attr: Node-level attribute-value mapping (``with``-block use).
edge_attr: Edge-level attribute-value mapping (``with``-block use).
body: Verbatim lines to add to the subgraph ``body`` (``with``-block use).
See the :ref:`usage examples in the User Guide <subgraphs>`.
.. note::
If the ``name`` of the subgraph begins with ``'cluster'`` (all lowercase)
the layout engine will treat it as a special cluster subgraph.
"""
if graph is None:
return SubgraphContext(self, {'name': name,
'comment': comment,
'graph_attr': graph_attr,
'node_attr': node_attr,
'edge_attr': edge_attr,
'body': body})
args = [name, comment, graph_attr, node_attr, edge_attr, body]
if not all(a is None for a in args):
raise ValueError('graph must be sole argument of subgraph()')
if graph.directed != self.directed:
raise ValueError('%r cannot add subgraph of different kind:'
' %r' % (self, graph))
lines = ['\t' + line for line in graph.__iter__(subgraph=True)]
self.body.extend(lines)
class SubgraphContext(object):
"""Return a blank instance of the parent and add as subgraph on exit."""
def __init__(self, parent, kwargs):
self.parent = parent
self.graph = parent.__class__(**kwargs)
def __enter__(self):
return self.graph
def __exit__(self, type_, value, traceback):
if type_ is None:
self.parent.subgraph(self.graph)
class Graph(Dot):
"""Graph source code in the DOT language.
Args:
name: Graph name used in the source code.
comment: Comment added to the first line of the source.
filename: Filename for saving the source (defaults to ``name`` + ``'.gv'``).
directory: (Sub)directory for source saving and rendering.
format: Rendering output format (``'pdf'``, ``'png'``, ...).
engine: Layout command used (``'dot'``, ``'neato'``, ...).
encoding: Encoding for saving the source.
graph_attr: Mapping of ``(attribute, value)`` pairs for the graph.
node_attr: Mapping of ``(attribute, value)`` pairs set for all nodes.
edge_attr: Mapping of ``(attribute, value)`` pairs set for all edges.
body: Iterable of verbatim lines to add to the graph ``body``.
strict (bool): Rendering should merge multi-edges.
Note:
All parameters are optional and can be changed under their
corresponding attribute name after instance creation.
"""
_head = 'graph %s{'
_head_strict = 'strict %s' % _head
_edge = '\t%s -- %s%s'
_edge_plain = _edge % ('%s', '%s', '')
@property
def directed(self):
"""``False``"""
return False
class Digraph(Dot):
"""Directed graph source code in the DOT language."""
if Graph.__doc__ is not None:
__doc__ += Graph.__doc__.partition('.')[2]
_head = 'digraph %s{'
_head_strict = 'strict %s' % _head
_edge = '\t%s -> %s%s'
_edge_plain = _edge % ('%s', '%s', '')
@property
def directed(self):
"""``True``"""
return True

View File

@ -1,311 +0,0 @@
# files.py - save, render, view
"""Save DOT code objects, render with Graphviz dot, and open in viewer."""
import os
import io
import codecs
import locale
import logging
from ._compat import text_type
from . import backend
from . import tools
__all__ = ['File', 'Source']
ENCODING = 'utf-8'
log = logging.getLogger(__name__)
class Base(object):
_format = 'pdf'
_engine = 'dot'
_encoding = ENCODING
@property
def format(self):
"""The output format used for rendering (``'pdf'``, ``'png'``, ...)."""
return self._format
@format.setter
def format(self, format):
format = format.lower()
if format not in backend.FORMATS:
raise ValueError('unknown format: %r' % format)
self._format = format
@property
def engine(self):
"""The layout commmand used for rendering (``'dot'``, ``'neato'``, ...)."""
return self._engine
@engine.setter
def engine(self, engine):
engine = engine.lower()
if engine not in backend.ENGINES:
raise ValueError('unknown engine: %r' % engine)
self._engine = engine
@property
def encoding(self):
"""The encoding for the saved source file."""
return self._encoding
@encoding.setter
def encoding(self, encoding):
if encoding is None:
encoding = locale.getpreferredencoding()
codecs.lookup(encoding) # raise early
self._encoding = encoding
def copy(self):
"""Return a copied instance of the object.
Returns:
An independent copy of the current object.
"""
kwargs = self._kwargs()
return self.__class__(**kwargs)
def _kwargs(self):
ns = self.__dict__
return {a[1:]: ns[a] for a in ('_format', '_engine', '_encoding')
if a in ns}
class File(Base):
directory = ''
_default_extension = 'gv'
def __init__(self, filename=None, directory=None,
format=None, engine=None, encoding=ENCODING):
if filename is None:
name = getattr(self, 'name', None) or self.__class__.__name__
filename = '%s.%s' % (name, self._default_extension)
self.filename = filename
if directory is not None:
self.directory = directory
if format is not None:
self.format = format
if engine is not None:
self.engine = engine
self.encoding = encoding
def _kwargs(self):
result = super(File, self)._kwargs()
result['filename'] = self.filename
if 'directory' in self.__dict__:
result['directory'] = self.directory
return result
def _repr_svg_(self):
return self.pipe(format='svg').decode(self._encoding)
def pipe(self, format=None, renderer=None, formatter=None, quiet=False):
"""Return the source piped through the Graphviz layout command.
Args:
format: The output format used for rendering (``'pdf'``, ``'png'``, etc.).
renderer: The output renderer used for rendering (``'cairo'``, ``'gd'``, ...).
formatter: The output formatter used for rendering (``'cairo'``, ``'gd'``, ...).
quiet (bool): Suppress ``stderr`` output from the layout subprocess.
Returns:
Binary (encoded) stdout of the layout command.
Raises:
ValueError: If ``format``, ``renderer``, or ``formatter`` are not known.
graphviz.RequiredArgumentError: If ``formatter`` is given but ``renderer`` is None.
graphviz.ExecutableNotFound: If the Graphviz executable is not found.
subprocess.CalledProcessError: If the exit status is non-zero.
"""
if format is None:
format = self._format
data = text_type(self.source).encode(self._encoding)
out = backend.pipe(self._engine, format, data,
renderer=renderer, formatter=formatter,
quiet=quiet)
return out
@property
def filepath(self):
return os.path.join(self.directory, self.filename)
def save(self, filename=None, directory=None):
"""Save the DOT source to file. Ensure the file ends with a newline.
Args:
filename: Filename for saving the source (defaults to ``name`` + ``'.gv'``)
directory: (Sub)directory for source saving and rendering.
Returns:
The (possibly relative) path of the saved source file.
"""
if filename is not None:
self.filename = filename
if directory is not None:
self.directory = directory
filepath = self.filepath
tools.mkdirs(filepath)
data = text_type(self.source)
log.debug('write %d bytes to %r', len(data), filepath)
with io.open(filepath, 'w', encoding=self.encoding) as fd:
fd.write(data)
if not data.endswith(u'\n'):
fd.write(u'\n')
return filepath
def render(self, filename=None, directory=None, view=False, cleanup=False,
format=None, renderer=None, formatter=None,
quiet=False, quiet_view=False):
"""Save the source to file and render with the Graphviz engine.
Args:
filename: Filename for saving the source (defaults to ``name`` + ``'.gv'``)
directory: (Sub)directory for source saving and rendering.
view (bool): Open the rendered result with the default application.
cleanup (bool): Delete the source file after rendering.
format: The output format used for rendering (``'pdf'``, ``'png'``, etc.).
renderer: The output renderer used for rendering (``'cairo'``, ``'gd'``, ...).
formatter: The output formatter used for rendering (``'cairo'``, ``'gd'``, ...).
quiet (bool): Suppress ``stderr`` output from the layout subprocess.
quiet_view (bool): Suppress ``stderr`` output from the viewer process
(implies ``view=True``, ineffective on Windows).
Returns:
The (possibly relative) path of the rendered file.
Raises:
ValueError: If ``format``, ``renderer``, or ``formatter`` are not known.
graphviz.RequiredArgumentError: If ``formatter`` is given but ``renderer`` is None.
graphviz.ExecutableNotFound: If the Graphviz executable is not found.
subprocess.CalledProcessError: If the exit status is non-zero.
RuntimeError: If viewer opening is requested but not supported.
The layout command is started from the directory of ``filepath``, so that
references to external files (e.g. ``[image=...]``) can be given as paths
relative to the DOT source file.
"""
filepath = self.save(filename, directory)
if format is None:
format = self._format
rendered = backend.render(self._engine, format, filepath,
renderer=renderer, formatter=formatter,
quiet=quiet)
if cleanup:
log.debug('delete %r', filepath)
os.remove(filepath)
if quiet_view or view:
self._view(rendered, self._format, quiet_view)
return rendered
def view(self, filename=None, directory=None, cleanup=False,
quiet=False, quiet_view=False):
"""Save the source to file, open the rendered result in a viewer.
Args:
filename: Filename for saving the source (defaults to ``name`` + ``'.gv'``)
directory: (Sub)directory for source saving and rendering.
cleanup (bool): Delete the source file after rendering.
quiet (bool): Suppress ``stderr`` output from the layout subprocess.
quiet_view (bool): Suppress ``stderr`` output from the viewer process
(ineffective on Windows).
Returns:
The (possibly relative) path of the rendered file.
Raises:
graphviz.ExecutableNotFound: If the Graphviz executable is not found.
subprocess.CalledProcessError: If the exit status is non-zero.
RuntimeError: If opening the viewer is not supported.
Short-cut method for calling :meth:`.render` with ``view=True``.
"""
return self.render(filename=filename, directory=directory,
view=True, cleanup=cleanup,
quiet=quiet, quiet_view=quiet_view)
def _view(self, filepath, format, quiet):
"""Start the right viewer based on file format and platform."""
methodnames = [
'_view_%s_%s' % (format, backend.PLATFORM),
'_view_%s' % backend.PLATFORM,
]
for name in methodnames:
view_method = getattr(self, name, None)
if view_method is not None:
break
else:
raise RuntimeError('%r has no built-in viewer support for %r'
' on %r platform' % (self.__class__, format,
backend.PLATFORM))
view_method(filepath, quiet)
_view_darwin = staticmethod(backend.view.darwin)
_view_freebsd = staticmethod(backend.view.freebsd)
_view_linux = staticmethod(backend.view.linux)
_view_windows = staticmethod(backend.view.windows)
class Source(File):
"""Verbatim DOT source code string to be rendered by Graphviz.
Args:
source: The verbatim DOT source code string.
filename: Filename for saving the source (defaults to ``'Source.gv'``).
directory: (Sub)directory for source saving and rendering.
format: Rendering output format (``'pdf'``, ``'png'``, ...).
engine: Layout command used (``'dot'``, ``'neato'``, ...).
encoding: Encoding for saving the source.
Note:
All parameters except ``source`` are optional. All of them can be changed
under their corresponding attribute name after instance creation.
"""
@classmethod
def from_file(cls, filename, directory=None,
format=None, engine=None, encoding=ENCODING):
"""Return an instance with the source string read from the given file.
Args:
filename: Filename for loading/saving the source.
directory: (Sub)directory for source loading/saving and rendering.
format: Rendering output format (``'pdf'``, ``'png'``, ...).
engine: Layout command used (``'dot'``, ``'neato'``, ...).
encoding: Encoding for loading/saving the source.
"""
filepath = os.path.join(directory or '', filename)
if encoding is None:
encoding = locale.getpreferredencoding()
log.debug('read %r with encoding %r', filepath, encoding)
with io.open(filepath, encoding=encoding) as fd:
source = fd.read()
return cls(source, filename, directory, format, engine, encoding)
def __init__(self, source, filename=None, directory=None,
format=None, engine=None, encoding=ENCODING):
super(Source, self).__init__(filename, directory,
format, engine, encoding)
self.source = source #: The verbatim DOT source code string.
def _kwargs(self):
result = super(Source, self)._kwargs()
result['source'] = self.source
return result

View File

@ -1,195 +0,0 @@
# lang.py - dot language creation helpers
"""Quote strings to be valid DOT identifiers, assemble attribute lists."""
import re
import collections
import functools
from . import _compat
from . import tools
__all__ = ['quote', 'quote_edge', 'a_list', 'attr_list', 'escape', 'nohtml']
# https://www.graphviz.org/doc/info/lang.html
# https://www.graphviz.org/doc/info/attrs.html#k:escString
HTML_STRING = re.compile(r'<.*>$', re.DOTALL)
ID = re.compile(r'([a-zA-Z_][a-zA-Z0-9_]*|-?(\.[0-9]+|[0-9]+(\.[0-9]*)?))$')
KEYWORDS = {'node', 'edge', 'graph', 'digraph', 'subgraph', 'strict'}
COMPASS = {'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'c', '_'} # TODO
QUOTE_OPTIONAL_BACKSLASHES = re.compile(r'(?P<bs>(?:\\\\)*)'
r'\\?(?P<quote>")')
ESCAPE_UNESCAPED_QUOTES = functools.partial(QUOTE_OPTIONAL_BACKSLASHES.sub,
r'\g<bs>\\\g<quote>')
def quote(identifier,
is_html_string=HTML_STRING.match,
is_valid_id=ID.match, dot_keywords=KEYWORDS,
escape_unescaped_quotes=ESCAPE_UNESCAPED_QUOTES):
r"""Return DOT identifier from string, quote if needed.
>>> quote('')
'""'
>>> quote('spam')
'spam'
>>> quote('spam spam')
'"spam spam"'
>>> quote('-4.2')
'-4.2'
>>> quote('.42')
'.42'
>>> quote('<<b>spam</b>>')
'<<b>spam</b>>'
>>> quote(nohtml('<>'))
'"<>"'
>>> print(quote('"'))
"\""
>>> print(quote('\\"'))
"\""
>>> print(quote('\\\\"'))
"\\\""
>>> print(quote('\\\\\\"'))
"\\\""
"""
if is_html_string(identifier) and not isinstance(identifier, NoHtml):
pass
elif not is_valid_id(identifier) or identifier.lower() in dot_keywords:
return '"%s"' % escape_unescaped_quotes(identifier)
return identifier
def quote_edge(identifier):
"""Return DOT edge statement node_id from string, quote if needed.
>>> quote_edge('spam')
'spam'
>>> quote_edge('spam spam:eggs eggs')
'"spam spam":"eggs eggs"'
>>> quote_edge('spam:eggs:s')
'spam:eggs:s'
"""
node, _, rest = identifier.partition(':')
parts = [quote(node)]
if rest:
port, _, compass = rest.partition(':')
parts.append(quote(port))
if compass:
parts.append(compass)
return ':'.join(parts)
def a_list(label=None, kwargs=None, attributes=None):
"""Return assembled DOT a_list string.
>>> a_list('spam', {'spam': None, 'ham': 'ham ham', 'eggs': ''})
'label=spam eggs="" ham="ham ham"'
"""
result = ['label=%s' % quote(label)] if label is not None else []
if kwargs:
items = ['%s=%s' % (quote(k), quote(v))
for k, v in tools.mapping_items(kwargs) if v is not None]
result.extend(items)
if attributes:
if hasattr(attributes, 'items'):
attributes = tools.mapping_items(attributes)
items = ['%s=%s' % (quote(k), quote(v))
for k, v in attributes if v is not None]
result.extend(items)
return ' '.join(result)
def attr_list(label=None, kwargs=None, attributes=None):
"""Return assembled DOT attribute list string.
Sorts ``kwargs`` and ``attributes`` if they are plain dicts (to avoid
unpredictable order from hash randomization in Python 3 versions).
>>> attr_list()
''
>>> attr_list('spam spam', kwargs={'eggs': 'eggs', 'ham': 'ham ham'})
' [label="spam spam" eggs=eggs ham="ham ham"]'
>>> attr_list(kwargs={'spam': None, 'eggs': ''})
' [eggs=""]'
"""
content = a_list(label, kwargs, attributes)
if not content:
return ''
return ' [%s]' % content
def escape(s):
r"""Return ``s`` as literal disabling special meaning of backslashes and ``'<...>'``.
see also https://www.graphviz.org/doc/info/attrs.html#k:escString
Args:
s: String in which backslashes and ``'<...>'`` should be treated as literal.
Raises:
TypeError: If ``s`` is not a ``str`` on Python 3, or a ``str``/``unicode`` on Python 2.
>>> print(escape(r'\l'))
\\l
"""
return nohtml(s.replace('\\', '\\\\'))
class NoHtml(object):
"""Mixin for string subclasses disabling fall-through of ``'<...>'``."""
__slots__ = ()
_doc = "%s subclass that does not treat ``'<...>'`` as DOT HTML string."
@classmethod
def _subcls(cls, other):
name = '%s_%s' % (cls.__name__, other.__name__)
bases = (other, cls)
ns = {'__doc__': cls._doc % other.__name__}
return type(name, bases, ns)
NOHTML = collections.OrderedDict((c, NoHtml._subcls(c)) for c in _compat.string_classes)
def nohtml(s):
"""Return copy of ``s`` that will not treat ``'<...>'`` as DOT HTML string in quoting.
Args:
s: String in which leading ``'<'`` and trailing ``'>'`` should be treated as literal.
Raises:
TypeError: If ``s`` is not a ``str`` on Python 3, or a ``str``/``unicode`` on Python 2.
>>> quote('<>-*-<>')
'<>-*-<>'
>>> quote(nohtml('<>-*-<>'))
'"<>-*-<>"'
"""
try:
subcls = NOHTML[type(s)]
except KeyError:
raise TypeError('%r does not have one of the required types:'
' %r' % (s, list(NOHTML)))
return subcls(s)

View File

@ -1,47 +0,0 @@
# tools.py - generic helpers
import os
from . import _compat
__all__ = ['attach', 'mkdirs', 'mapping_items']
def attach(object, name):
"""Return a decorator doing ``setattr(object, name)`` with its argument.
>>> spam = type('Spam', (object,), {})()
>>> @attach(spam, 'eggs')
... def func():
... pass
>>> spam.eggs # doctest: +ELLIPSIS
<function func at 0x...>
"""
def decorator(func):
setattr(object, name, func)
return func
return decorator
def mkdirs(filename, mode=0o777):
"""Recursively create directories up to the path of ``filename`` as needed."""
dirname = os.path.dirname(filename)
if not dirname:
return
_compat.makedirs(dirname, mode=mode, exist_ok=True)
def mapping_items(mapping):
"""Return an iterator over the ``mapping`` items, sort if it's a plain dict.
>>> list(mapping_items({'spam': 0, 'ham': 1, 'eggs': 2}))
[('eggs', 2), ('ham', 1), ('spam', 0)]
>>> from collections import OrderedDict
>>> list(mapping_items(OrderedDict(enumerate(['spam', 'ham', 'eggs']))))
[(0, 'spam'), (1, 'ham'), (2, 'eggs')]
"""
result = _compat.iteritems(mapping)
if type(mapping) is dict:
result = iter(sorted(result))
return result

View File

@ -1,2 +1,3 @@
__pycache__/
_test/
wv-env/

141
src/wireviz/DataClasses.py Normal file
View File

@ -0,0 +1,141 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Optional, List, Any
from dataclasses import dataclass, field
from wireviz.wv_helper import int2tuple
from wireviz import wv_colors
@dataclass
class Connector:
name: str
category: Optional[str] = None
type: Optional[str] = None
subtype: Optional[str] = None
pincount: Optional[int] = None
notes: Optional[str] = None
pinout: List[Any] = field(default_factory=list)
pinnumbers: List[Any] = field(default_factory=list)
color: Optional[str] = None
show_name: bool = True
show_pincount: bool = True
hide_disconnected_pins: bool = False
def __post_init__(self):
self.ports_left = False
self.ports_right = False
self.loops = []
self.visible_pins = {}
if self.pincount is None:
if self.pinout:
self.pincount = len(self.pinout)
elif self.pinnumbers:
self.pincount = len(self.pinnumbers)
elif self.category == 'ferrule':
self.pincount = 1
else:
raise Exception('You need to specify at least one, pincount, pinout or pinnumbers')
if self.pinout and self.pinnumbers:
if len(self.pinout) != len(self.pinnumbers):
raise Exception('Given pinout and pinnumbers size mismatch')
# create default lists for pinnumbers (sequential) and pinouts (blank) if not specified
if not self.pinnumbers:
self.pinnumbers = list(range(1, self.pincount + 1))
if not self.pinout:
self.pinout = [''] * self.pincount
def loop(self, from_pin, to_pin):
self.loops.append((from_pin, to_pin))
if self.hide_disconnected_pins:
self.visible_pins[from_pin] = True
self.visible_pins[to_pin] = True
def activate_pin(self, pin):
self.visible_pins[pin] = True
@dataclass
class Cable:
name: str
category: Optional[str] = None
type: Optional[str] = None
gauge: Optional[float] = None
gauge_unit: Optional[str] = None
show_equiv: bool = False
length: float = 0
wirecount: Optional[int] = None
shield: bool = False
notes: Optional[str] = None
colors: List[Any] = field(default_factory=list)
color_code: Optional[str] = None
show_name: bool = True
show_wirecount: bool = True
def __post_init__(self):
if isinstance(self.gauge, str): # gauge and unit specified
try:
g, u = self.gauge.split(' ')
except Exception:
raise Exception('Gauge must be a number, or number and unit separated by a space')
self.gauge = g
if u.upper() == 'AWG':
self.gauge_unit = u.upper()
else:
self.gauge_unit = u.replace('mm2', 'mm\u00B2')
elif self.gauge is not None: # gauge specified, assume mm2
if self.gauge_unit is None:
self.gauge_unit = 'mm\u00B2'
else:
pass # gauge not specified
self.connections = []
if self.wirecount: # number of wires explicitly defined
if self.colors: # use custom color palette (partly or looped if needed)
pass
elif self.color_code: # use standard color palette (partly or looped if needed)
if self.color_code not in wv_colors.COLOR_CODES:
raise Exception('Unknown color code')
self.colors = wv_colors.COLOR_CODES[self.color_code]
else: # no colors defined, add dummy colors
self.colors = [''] * self.wirecount
# make color code loop around if more wires than colors
if self.wirecount > len(self.colors):
m = self.wirecount // len(self.colors) + 1
self.colors = self.colors * int(m)
# cut off excess after looping
self.colors = self.colors[:self.wirecount]
else: # wirecount implicit in length of color list
if not self.colors:
raise Exception('Unknown number of wires. Must specify wirecount or colors (implicit length)')
self.wirecount = len(self.colors)
# for BOM generation
self.wirecount_and_shield = (self.wirecount, self.shield)
def connect(self, from_name, from_pin, via_pin, to_name, to_pin):
from_pin = int2tuple(from_pin)
via_pin = int2tuple(via_pin)
to_pin = int2tuple(to_pin)
if len(from_pin) != len(to_pin):
raise Exception('from_pin must have the same number of elements as to_pin')
for i, _ in enumerate(from_pin):
# self.connections.append((from_name, from_pin[i], via_pin[i], to_name, to_pin[i]))
self.connections.append(Connection(from_name, from_pin[i], via_pin[i], to_name, to_pin[i]))
@dataclass
class Connection:
from_name: Any
from_port: Any
via_port: Any
to_name: Any
to_port: Any

331
src/wireviz/Harness.py Normal file
View File

@ -0,0 +1,331 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from wireviz.DataClasses import Connector, Cable
from graphviz import Graph
from wireviz import wv_colors
from wireviz.wv_helper import awg_equiv, mm2_equiv, tuplelist2tsv, nested, flatten2d
from collections import Counter
from typing import List
class Harness:
def __init__(self):
self.color_mode = 'SHORT'
self.connectors = {}
self.cables = {}
def add_connector(self, name, *args, **kwargs):
self.connectors[name] = Connector(name, *args, **kwargs)
def add_cable(self, name, *args, **kwargs):
self.cables[name] = Cable(name, *args, **kwargs)
def loop(self, connector_name, from_pin, to_pin):
self.connectors[connector_name].loop(from_pin, to_pin)
def connect(self, from_name, from_pin, via_name, via_pin, to_name, to_pin):
self.cables[via_name].connect(from_name, from_pin, via_pin, to_name, to_pin)
if from_name in self.connectors:
self.connectors[from_name].activate_pin(from_pin)
if to_name in self.connectors:
self.connectors[to_name].activate_pin(to_pin)
def create_graph(self):
dot = Graph()
dot.body.append('// Graph generated by WireViz')
dot.body.append('// https://github.com/formatc1702/WireViz')
font = 'arial'
dot.attr('graph', rankdir='LR',
ranksep='2',
bgcolor='white',
nodesep='0.33',
fontname=font)
dot.attr('node', shape='record',
style='filled',
fillcolor='white',
fontname=font)
dot.attr('edge', style='bold',
fontname=font)
# prepare ports on connectors depending on which side they will connect
for _, cable in self.cables.items():
for connection in cable.connections:
if connection.from_port is not None: # connect to left
self.connectors[connection.from_name].ports_right = True
if connection.to_port is not None: # connect to right
self.connectors[connection.to_name].ports_left = True
for key, connector in self.connectors.items():
if connector.category == 'ferrule':
subtype = f', {connector.subtype}' if connector.subtype else ''
color = wv_colors.translate_color(connector.color, self.color_mode) if connector.color else ''
infostring = f'{connector.type}{subtype} {color}'
infostring_l = infostring if connector.ports_right else ''
infostring_r = infostring if connector.ports_left else ''
# INFO: Leaving this one as a string.format form because f-strings do not work well with triple quotes
colorbar = f'<TD BGCOLOR="{wv_colors.translate_color(connector.color, "HEX")}" BORDER="1" SIDES="LR" WIDTH="4"></TD>' if connector.color else ''
dot.node(key, shape='none',
style='filled',
margin='0',
orientation='0' if connector.ports_left else '180',
label='''<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="2"><TR>
<TD PORT="p1l"> {infostring_l} </TD>
{colorbar}
<TD PORT="p1r"> {infostring_r} </TD>
</TR></TABLE>
>'''.format(infostring_l=infostring_l, infostring_r=infostring_r, colorbar=colorbar))
else: # not a ferrule
attributes = [connector.type,
connector.subtype,
f'{connector.pincount}-pin' if connector.show_pincount else'']
pinouts = [[], [], []]
for pinnumber, pinname in zip(connector.pinnumbers, connector.pinout):
if connector.hide_disconnected_pins and not connector.visible_pins.get(pinnumber, False):
continue
pinouts[1].append(pinname)
if connector.ports_left:
pinouts[0].append(f'<p{pinnumber}l>{pinnumber}')
if connector.ports_right:
pinouts[2].append(f'<p{pinnumber}r>{pinnumber}')
label = [connector.name if connector.show_name else '', attributes, pinouts, connector.notes]
dot.node(key, label=nested(label))
if len(connector.loops) > 0:
dot.attr('edge', color='#000000:#ffffff:#000000')
if connector.ports_left:
loop_side = 'l'
loop_dir = 'w'
elif connector.ports_right:
loop_side = 'r'
loop_dir = 'e'
else:
raise Exception('No side for loops')
for loop in connector.loops:
dot.edge(f'{connector.name}:p{loop[0]}{loop_side}:{loop_dir}',
f'{connector.name}:p{loop[1]}{loop_side}:{loop_dir}')
for _, cable in self.cables.items():
awg_fmt = ''
if cable.show_equiv:
# Only convert units we actually know about, i.e. currently
# mm2 and awg --- other units _are_ technically allowed,
# and passed through as-is.
if cable.gauge_unit =='mm\u00B2':
awg_fmt = f' ({awg_equiv(cable.gauge)} AWG)'
elif cable.gauge_unit.upper() == 'AWG':
awg_fmt = f' ({mm2_equiv(cable.gauge)} mm\u00B2)'
attributes = [f'{len(cable.colors)}x' if cable.show_wirecount else '',
f'{cable.gauge} {cable.gauge_unit}{awg_fmt}' if cable.gauge else '',
'+ S' if cable.shield else '',
f'{cable.length} m' if cable.length > 0 else '']
attributes = list(filter(None, attributes))
html = '<table border="0" cellspacing="0" cellpadding="0"><tr><td>' # main table
html = f'{html}<table border="0" cellspacing="0" cellpadding="3" cellborder="1">' # name+attributes table
if cable.show_name:
html = f'{html}<tr><td colspan="{len(attributes)}">{cable.name}</td></tr>'
html = f'{html}<tr>' # attribute row
for attrib in attributes:
html = f'{html}<td>{attrib}</td>'
html = f'{html}</tr>' # attribute row
html = f'{html}</table></td></tr>' # name+attributes table
html = f'{html}<tr><td>&nbsp;</td></tr>' # spacer between attributes and wires
html = f'{html}<tr><td><table border="0" cellspacing="0" cellborder="0">' # conductor table
for i, connection in enumerate(cable.colors, 1):
p = []
p.append(f'<!-- {i}_in -->')
p.append(wv_colors.translate_color(connection, self.color_mode))
p.append(f'<!-- {i}_out -->')
html = f'{html}<tr>'
for bla in p:
html = f'{html}<td>{bla}</td>'
html = f'{html}</tr>'
bgcolor = wv_colors.translate_color(connection, 'hex')
bgcolor = bgcolor if bgcolor != '' else '#ffffff'
html = f'{html}<tr><td colspan="{len(p)}" cellpadding="0" height="6" bgcolor="{bgcolor}" border="2" sides="tb" port="w{i}"></td></tr>'
if cable.shield:
p = ['<!-- s_in -->', 'Shield', '<!-- s_out -->']
html = f'{html}<tr><td>&nbsp;</td></tr>' # spacer
html = f'{html}<tr>'
for bla in p:
html = html + f'<td>{bla}</td>'
html = f'{html}</tr>'
html = f'{html}<tr><td colspan="{len(p)}" cellpadding="0" height="6" border="2" sides="b" port="ws"></td></tr>'
html = f'{html}<tr><td>&nbsp;</td></tr>' # spacer at the end
html = f'{html}</table>' # conductor table
html = f'{html}</td></tr>' # main table
if cable.notes:
html = f'{html}<tr><td cellpadding="3">{cable.notes}</td></tr>' # notes table
html = f'{html}<tr><td>&nbsp;</td></tr>' # spacer at the end
html = f'{html}</table>' # main table
# connections
for connection in cable.connections:
if isinstance(connection.via_port, int): # check if it's an actual wire and not a shield
search_color = cable.colors[connection.via_port - 1]
if search_color in wv_colors.color_hex:
dot.attr('edge', color=f'#000000:{wv_colors.color_hex[search_color]}:#000000')
else: # color name not found
dot.attr('edge', color='#000000:#ffffff:#000000')
else: # it's a shield connection
dot.attr('edge', color='#000000')
if connection.from_port is not None: # connect to left
from_ferrule = self.connectors[connection.from_name].category == 'ferrule'
port = f':p{connection.from_port}r' if not from_ferrule else ''
code_left_1 = f'{connection.from_name}{port}:e'
code_left_2 = f'{cable.name}:w{connection.via_port}:w'
dot.edge(code_left_1, code_left_2)
from_string = f'{connection.from_name}:{connection.from_port}' if not from_ferrule else ''
html = html.replace(f'<!-- {connection.via_port}_in -->', from_string)
if connection.to_port is not None: # connect to right
to_ferrule = self.connectors[connection.to_name].category == 'ferrule'
code_right_1 = f'{cable.name}:w{connection.via_port}:e'
to_port = f':p{connection.to_port}l' if not to_ferrule else ''
code_right_2 = f'{connection.to_name}{to_port}:w'
dot.edge(code_right_1, code_right_2)
to_string = f'{connection.to_name}:{connection.to_port}' if not to_ferrule else ''
html = html.replace(f'<!-- {connection.via_port}_out -->', to_string)
dot.node(cable.name, label=f'<{html}>', shape='box',
style='filled,dashed' if cable.category == 'bundle' else '', margin='0', fillcolor='white')
return dot
def output(self, filename, directory='_output', view=False, cleanup=True, fmt='pdf', gen_bom=False):
# graphical output
graph = self.create_graph()
for f in fmt:
graph.format = f
graph.render(filename=filename, directory=directory, view=view, cleanup=cleanup)
graph.save(filename=f'{filename}.gv', directory=directory)
# bom output
bom_list = self.bom_list()
with open(f'{filename}.bom.tsv', 'w') as file:
file.write(tuplelist2tsv(bom_list))
# HTML output
with open(f'{filename}.html', 'w') as file:
file.write('<html><body style="font-family:Arial">')
file.write('<h1>Diagram</h1>')
with open(f'{filename}.svg') as svg:
for svgdata in svg:
file.write(svgdata)
file.write('<h1>Bill of Materials</h1>')
listy = flatten2d(bom_list)
file.write('<table style="border:1px solid #000000; font-size: 14pt; border-spacing: 0px">')
file.write('<tr>')
for item in listy[0]:
file.write(f'<th align="left" style="border:1px solid #000000; padding: 8px">{item}</th>')
file.write('</tr>')
for row in listy[1:]:
file.write('<tr>')
for i, item in enumerate(row):
align = 'align="right"' if listy[0][i] == 'Qty' else ''
file.write(f'<td {align} style="border:1px solid #000000; padding: 4px">{item}</td>')
file.write('</tr>')
file.write('</table>')
file.write('</body></html>')
def bom(self):
bom = []
bom_connectors = []
bom_cables = []
# connectors
types = Counter([(v.type, v.subtype, v.pincount) for v in self.connectors.values()])
for maintype in types:
items = {k: v for k, v in self.connectors.items() if (v.type, v.subtype, v.pincount) == maintype}
shared = next(iter(items.values()))
designators = list(items.keys())
designators.sort()
conn_type = f', {shared.type}' if shared.type else ''
conn_subtype = f', {shared.subtype}' if shared.subtype else ''
conn_pincount = f', {shared.pincount} pins' if shared.category != 'ferrule' else ''
conn_color = f', {shared.color}' if shared.color else ''
name = f'Connector{conn_type}{conn_subtype}{conn_pincount}{conn_color}'
item = {'item': name, 'qty': len(designators), 'unit': '',
'designators': designators if shared.category != 'ferrule' else ''}
bom_connectors.append(item)
bom_connectors = sorted(bom_connectors, key=lambda k: k['item']) # https://stackoverflow.com/a/73050
bom.extend(bom_connectors)
# cables
types = Counter([(v.category, v.gauge, v.gauge_unit, v.wirecount, v.shield) for v in self.cables.values()])
for maintype in types:
items = {k: v for k, v in self.cables.items() if (
v.category, v.gauge, v.gauge_unit, v.wirecount, v.shield) == maintype}
shared = next(iter(items.values()))
if shared.category != 'bundle':
designators = list(items.keys())
designators.sort()
total_length = sum(i.length for i in items.values())
gauge_name = f' x {shared.gauge} {shared.gauge_unit}'if shared.gauge else ' wires'
shield_name = ' shielded' if shared.shield else ''
name = f'Cable, {shared.wirecount}{gauge_name}{shield_name}'
item = {'item': name, 'qty': round(total_length, 3), 'unit': 'm', 'designators': designators}
bom_cables.append(item)
# bundles (ignores wirecount)
wirelist = []
# list all cables again, since bundles are represented as wires internally, with the category='bundle' set
types = Counter([(v.category, v.gauge, v.gauge_unit, v.length) for v in self.cables.values()])
for maintype in types:
items = {k: v for k, v in self.cables.items() if (v.category, v.gauge, v.gauge_unit, v.length) == maintype}
shared = next(iter(items.values()))
# filter out cables that are not bundles
if shared.category == 'bundle':
for bundle in items.values():
# add each wire from each bundle to the wirelist
for color in bundle.colors:
wirelist.append({'gauge': shared.gauge, 'gauge_unit': shared.gauge_unit,
'length': shared.length, 'color': color, 'designator': bundle.name})
# join similar wires from all the bundles to a single BOM item
types = Counter([(v['gauge'], v['gauge_unit'], v['color']) for v in wirelist])
for maintype in types:
items = [v for v in wirelist if (v['gauge'], v['gauge_unit'], v['color']) == maintype]
shared = items[0]
designators = [i['designator'] for i in items]
# remove duplicates
designators = list(dict.fromkeys(designators))
designators.sort()
total_length = sum(i['length'] for i in items)
gauge_name = f', {shared["gauge"]} {shared["gauge_unit"]}' if shared['gauge'] else ''
gauge_color = f', {shared["color"]}' if shared['color'] != '' else ''
name = f'Wire{gauge_name}{gauge_color}'
item = {'item': name, 'qty': round(total_length, 3), 'unit': 'm', 'designators': designators}
bom_cables.append(item)
bom_cables = sorted(bom_cables, key=lambda k: k['item']) # https://stackoverflow.com/a/73050
bom.extend(bom_cables)
return bom
def bom_list(self):
bom = self.bom()
keys = ['item', 'qty', 'unit', 'designators']
bom_list = []
bom_list.append([k.capitalize() for k in keys]) # create header row with keys
for item in bom:
item_list = [item.get(key, '') for key in keys] # fill missing values with blanks
for i, subitem in enumerate(item_list):
if isinstance(subitem, List): # convert any lists into comma separated strings
item_list[i] = ', '.join(subitem)
bom_list.append(item_list)
return bom_list

0
src/wireviz/__init__.py Normal file
View File

57
src/wireviz/build_examples.py Executable file
View File

@ -0,0 +1,57 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from wireviz import wireviz
demos = 2 # 2
examples = 9 # 9
tutorials = 7 # 7
if demos:
for i in range(1,demos+1):
fn = '../../examples/demo{:02d}.yml'.format(i)
print(fn)
wireviz.parse_file(fn, generate_bom=True)
if examples:
with open(os.path.abspath('../../examples/readme.md'), 'w') as file:
file.write('# Example gallery\n')
for i in range(1,examples+1):
fn = '../../examples/ex{:02d}.yml'.format(i)
print(fn)
wireviz.parse_file(fn, generate_bom=True)
file.write('## Example {:02d}\n'.format(i))
file.write('![](ex{:02d}.png)\n\n'.format(i))
file.write('[Source](ex{:02d}.yml) - [Bill of Materials](ex{:02d}.bom.tsv)\n\n\n'.format(i,i))
if tutorials:
with open(os.path.abspath('../../tutorial/readme.md'), 'w') as file:
file.write('# WireViz Tutorial\n')
for i in range(1,tutorials+1):
fn = '../../tutorial/tutorial{:02d}.yml'.format(i)
print(fn)
wireviz.parse_file(fn, generate_bom=True)
with open(os.path.abspath('../../tutorial/tutorial{:02d}.md'.format(i)), 'r') as info:
for line in info:
file.write(line.replace('## ', '## {} - '.format(i)))
file.write('\n[Source](tutorial{:02d}.yml):\n\n'.format(i))
with open(os.path.abspath('../../tutorial/tutorial{:02d}.yml'.format(i)), 'r') as src:
file.write('```yaml\n')
for line in src:
file.write(line)
file.write('```\n')
file.write('\n')
file.write('\nOutput:\n\n'.format(i))
file.write('![](tutorial{:02d}.png)\n\n'.format(i))
file.write('[Bill of Materials](tutorial{:02d}.bom.tsv)\n\n\n'.format(i))

239
src/wireviz/wireviz.py Executable file
View File

@ -0,0 +1,239 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import os
import sys
import yaml
if __name__ == '__main__':
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from wireviz.Harness import Harness
def parse(yaml_input, file_out=None, generate_bom=False):
yaml_data = yaml.safe_load(yaml_input)
def expand(yaml_data):
# yaml_data can be:
# - a singleton (normally str or int)
# - a list of str or int
# if str is of the format '#-#', it is treated as a range (inclusive) and expanded
output = []
if not isinstance(yaml_data, list):
yaml_data = [yaml_data]
for e in yaml_data:
e = str(e)
if '-' in e: # list of pins
a, b = tuple(map(int, e.split('-')))
if a < b:
for x in range(a, b + 1):
output.append(x)
elif a > b:
for x in range(a, b - 1, -1):
output.append(x)
elif a == b:
output.append(a)
else:
try:
x = int(e)
except Exception:
x = e
output.append(x)
return output
def check_designators(what, where):
for i, x in enumerate(what):
if x not in yaml_data[where[i]]:
return False
return True
harness = Harness()
# add items
sections = ['connectors', 'cables', 'ferrules', 'connections']
types = [dict, dict, dict, list]
for sec, ty in zip(sections, types):
if sec in yaml_data and type(yaml_data[sec]) == ty:
if len(yaml_data[sec]) > 0:
if ty == dict:
for key, o in yaml_data[sec].items():
if sec == 'connectors':
harness.add_connector(name=key, **o)
elif sec == 'cables':
harness.add_cable(name=key, **o)
elif sec == 'ferrules':
pass
else:
pass # section exists but is empty
else: # section does not exist, create empty section
if ty == dict:
yaml_data[sec] = {}
elif ty == list:
yaml_data[sec] = []
# add connections
ferrule_counter = 0
for connections in yaml_data['connections']:
if len(connections) == 3: # format: connector -- cable -- connector
for connection in connections:
if len(list(connection.keys())) != 1: # check that each entry in con has only one key, which is the designator
raise Exception('Too many keys')
from_name = list(connections[0].keys())[0]
via_name = list(connections[1].keys())[0]
to_name = list(connections[2].keys())[0]
if not check_designators([from_name, via_name, to_name], ('connectors', 'cables', 'connectors')):
print([from_name, via_name, to_name])
raise Exception('Bad connection definition (3)')
from_pins = expand(connections[0][from_name])
via_pins = expand(connections[1][via_name])
to_pins = expand(connections[2][to_name])
if len(from_pins) != len(via_pins) or len(via_pins) != len(to_pins):
raise Exception('List length mismatch')
for (from_pin, via_pin, to_pin) in zip(from_pins, via_pins, to_pins):
harness.connect(from_name, from_pin, via_name, via_pin, to_name, to_pin)
elif len(connections) == 2:
for connection in connections:
if type(connection) is dict:
if len(list(connection.keys())) != 1: # check that each entry in con has only one key, which is the designator
raise Exception('Too many keys')
# hack to make the format for ferrules compatible with the formats for connectors and cables
if type(connections[0]) == str:
name = connections[0]
connections[0] = {}
connections[0][name] = name
if type(connections[1]) == str:
name = connections[1]
connections[1] = {}
connections[1][name] = name
from_name = list(connections[0].keys())[0]
to_name = list(connections[1].keys())[0]
con_cbl = check_designators([from_name, to_name], ('connectors', 'cables'))
cbl_con = check_designators([from_name, to_name], ('cables', 'connectors'))
con_con = check_designators([from_name, to_name], ('connectors', 'connectors'))
fer_cbl = check_designators([from_name, to_name], ('ferrules', 'cables'))
cbl_fer = check_designators([from_name, to_name], ('cables', 'ferrules'))
if not con_cbl and not cbl_con and not con_con and not fer_cbl and not cbl_fer:
raise Exception('Wrong designators')
from_pins = expand(connections[0][from_name])
to_pins = expand(connections[1][to_name])
if con_cbl or cbl_con or con_con:
if len(from_pins) != len(to_pins):
raise Exception('List length mismatch')
if con_cbl or cbl_con:
for (from_pin, to_pin) in zip(from_pins, to_pins):
if con_cbl:
harness.connect(from_name, from_pin, to_name, to_pin, None, None)
else: # cbl_con
harness.connect(None, None, from_name, from_pin, to_name, to_pin)
elif con_con:
cocon_coname = list(connections[0].keys())[0]
from_pins = expand(connections[0][from_name])
to_pins = expand(connections[1][to_name])
for (from_pin, to_pin) in zip(from_pins, to_pins):
harness.loop(cocon_coname, from_pin, to_pin)
if fer_cbl or cbl_fer:
from_pins = expand(connections[0][from_name])
to_pins = expand(connections[1][to_name])
if fer_cbl:
ferrule_name = from_name
cable_name = to_name
cable_pins = to_pins
else:
ferrule_name = to_name
cable_name = from_name
cable_pins = from_pins
ferrule_params = yaml_data['ferrules'][ferrule_name]
for cable_pin in cable_pins:
ferrule_counter = ferrule_counter + 1
ferrule_id = f'_F{ferrule_counter}'
harness.add_connector(ferrule_id, category='ferrule', **ferrule_params)
if fer_cbl:
harness.connect(ferrule_id, 1, cable_name, cable_pin, None, None)
else:
harness.connect(None, None, cable_name, cable_pin, ferrule_id, 1)
else:
raise Exception('Wrong number of connection parameters')
harness.output(filename=file_out, fmt=('png', 'svg'), gen_bom=generate_bom, view=False)
def parse_file(yaml_file, file_out=None, generate_bom=False):
with open(yaml_file, 'r') as file:
yaml_input = file.read()
if not file_out:
fn, fext = os.path.splitext(yaml_file)
file_out = fn
file_out = os.path.abspath(file_out)
parse(yaml_input, file_out=file_out, generate_bom=generate_bom)
def parse_cmdline():
parser = argparse.ArgumentParser(
description='Generate cable and wiring harness documentation from YAML descriptions',
)
parser.add_argument('input_file', action='store', type=str, metavar='YAML_FILE')
parser.add_argument('-o', '--output_file', action='store', type=str, metavar='OUTPUT')
parser.add_argument('--generate-bom', action='store_true', default=True)
parser.add_argument('--prepend-file', action='store', type=str, metavar='YAML_FILE')
return parser.parse_args()
def main():
args = parse_cmdline()
if not os.path.exists(args.input_file):
print(f'Error: input file {args.input_file} inaccessible or does not exist, check path')
sys.exit(1)
with open(args.input_file) as fh:
yaml_input = fh.read()
if args.prepend_file:
if not os.path.exists(args.prepend_file):
print(f'Error: prepend input file {args.prepend_file} inaccessible or does not exist, check path')
sys.exit(1)
with open(args.prepend_file) as fh:
prepend = fh.read()
yaml_input = prepend + yaml_input
if not args.output_file:
file_out = args.input_file
pre, _ = os.path.splitext(file_out)
file_out = pre # extension will be added by graphviz output function
else:
file_out = args.output_file
file_out = os.path.abspath(file_out)
parse(yaml_input, file_out=file_out, generate_bom=args.generate_bom)
if __name__ == '__main__':
main()

78
src/wireviz/wv_colors.py Normal file
View File

@ -0,0 +1,78 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
COLOR_CODES = {
'DIN': ['WH', 'BN', 'GN', 'YE', 'GY', 'PK', 'BU', 'RD', 'BK', 'VT'], # ,'GYPK','RDBU','WHGN','BNGN','WHYE','YEBN','WHGY','GYBN','WHPK','PKBN'],
'IEC': ['BN', 'RD', 'OG', 'YE', 'GN', 'BU', 'VT', 'GY', 'WH', 'BK'],
'BW': ['BK', 'WH'],
}
color_hex = {
'BK': '#000000',
'WH': '#ffffff',
'GY': '#999999',
'PK': '#ff66cc',
'RD': '#ff0000',
'OG': '#ff8000',
'YE': '#ffff00',
'GN': '#00ff00',
'TQ': '#00ffff',
'BU': '#0066ff',
'VT': '#8000ff',
'BN': '#666600',
}
color_full = {
'BK': 'black',
'WH': 'white',
'GY': 'grey',
'PK': 'pink',
'RD': 'red',
'OG': 'orange',
'YE': 'yellow',
'GN': 'green',
'TQ': 'turquoise',
'BU': 'blue',
'VT': 'violet',
'BN': 'brown',
}
color_ger = {
'BK': 'sw',
'WH': 'ws',
'GY': 'gr',
'PK': 'rs',
'RD': 'rt',
'OG': 'or',
'YE': 'ge',
'GN': 'gn',
'TQ': 'tk',
'BU': 'bl',
'VT': 'vi',
'BN': 'br',
}
def translate_color(inp, color_mode):
if inp == '':
output = ''
else:
if color_mode == 'full':
output = color_full[inp].lower()
elif color_mode == 'FULL':
output = color_full[inp].upper()
elif color_mode == 'hex':
output = color_hex[inp].lower()
elif color_mode == 'HEX':
output = color_hex[inp].upper()
elif color_mode == 'ger':
output = color_ger[inp].lower()
elif color_mode == 'GER':
output = color_ger[inp].upper()
elif color_mode == 'short':
output = inp.lower()
elif color_mode == 'SHORT':
output = inp.upper()
else:
raise Exception('Unknown color mode')
return output

67
src/wireviz/wv_helper.py Normal file
View File

@ -0,0 +1,67 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import List
awg_equiv_table = {
'0.09': '28',
'0.14': '26',
'0.25': '24',
'0.34': '22',
'0.5': '21',
'0.75': '20',
'1': '18',
'1.5': '16',
'2.5': '14',
'4': '12',
'6': '10',
'10': '8',
'16': '6',
'25': '4',
'35': '2',
'50': '1',
}
mm2_equiv_table = {v:k for k,v in awg_equiv_table.items()}
def awg_equiv(mm2):
return awg_equiv_table.get(str(mm2), 'Unknown')
def mm2_equiv(awg):
return mm2_equiv_table.get(str(awg), 'Unknown')
def nested(inp):
l = []
for x in inp:
if isinstance(x, list):
if len(x) > 0:
n = nested(x)
if n != '':
l.append('{' + n + '}')
else:
if x is not None:
if x != '':
l.append(str(x))
return '|'.join(l)
def int2tuple(inp):
if isinstance(inp, tuple):
output = inp
else:
output = (inp,)
return output
def flatten2d(inp):
return [[str(item) if not isinstance(item, List) else ', '.join(item) for item in row] for row in inp]
def tuplelist2tsv(inp, header=None):
output = ''
if header is not None:
inp.insert(0, header)
inp = flatten2d(inp)
for row in inp:
output = output + '\t'.join(str(item) for item in row) + '\n'
return output

View File

@ -1,59 +0,0 @@
from typing import Any, List
def awg_equiv(mm2):
awg_equiv_table = {
'0.09': 28,
'0.14': 26,
'0.25': 24,
'0.34': 22,
'0.5': 21,
'0.75': 20,
'1': 18,
'1.5': 16,
'2.5': 14,
'4': 12,
'6': 10,
'10': 8,
'16': 6,
'25': 4,
}
k = str(mm2)
if k in awg_equiv_table:
return awg_equiv_table[k]
else:
return 'unknown'
def nested(input):
l = []
for x in input:
if isinstance(x, list):
if len(x) > 0:
n = nested(x)
if n != '':
l.append('{' + n + '}')
else:
if x is not None:
if x != '':
l.append(str(x))
s = '|'.join(l)
return s
def int2tuple(input):
if isinstance(input, tuple):
output = input
else:
output = (input,)
return output
def flatten2d(input):
output = [[str(item) if not isinstance(item, List) else ', '.join(item) for item in row] for row in input]
return output
def tuplelist2tsv(input, header=None):
output = ''
if header is not None:
input.insert(0, header)
input = flatten2d(input)
for row in input:
output = output + '\t'.join(str(item) for item in row) + '\n'
return output

View File

@ -1,427 +0,0 @@
from .error import *
from .tokens import *
from .events import *
from .nodes import *
from .loader import *
from .dumper import *
__version__ = '5.3.1'
try:
from .cyaml import *
__with_libyaml__ = True
except ImportError:
__with_libyaml__ = False
import io
#------------------------------------------------------------------------------
# Warnings control
#------------------------------------------------------------------------------
# 'Global' warnings state:
_warnings_enabled = {
'YAMLLoadWarning': True,
}
# Get or set global warnings' state
def warnings(settings=None):
if settings is None:
return _warnings_enabled
if type(settings) is dict:
for key in settings:
if key in _warnings_enabled:
_warnings_enabled[key] = settings[key]
# Warn when load() is called without Loader=...
class YAMLLoadWarning(RuntimeWarning):
pass
def load_warning(method):
if _warnings_enabled['YAMLLoadWarning'] is False:
return
import warnings
message = (
"calling yaml.%s() without Loader=... is deprecated, as the "
"default Loader is unsafe. Please read "
"https://msg.pyyaml.org/load for full details."
) % method
warnings.warn(message, YAMLLoadWarning, stacklevel=3)
#------------------------------------------------------------------------------
def scan(stream, Loader=Loader):
"""
Scan a YAML stream and produce scanning tokens.
"""
loader = Loader(stream)
try:
while loader.check_token():
yield loader.get_token()
finally:
loader.dispose()
def parse(stream, Loader=Loader):
"""
Parse a YAML stream and produce parsing events.
"""
loader = Loader(stream)
try:
while loader.check_event():
yield loader.get_event()
finally:
loader.dispose()
def compose(stream, Loader=Loader):
"""
Parse the first YAML document in a stream
and produce the corresponding representation tree.
"""
loader = Loader(stream)
try:
return loader.get_single_node()
finally:
loader.dispose()
def compose_all(stream, Loader=Loader):
"""
Parse all YAML documents in a stream
and produce corresponding representation trees.
"""
loader = Loader(stream)
try:
while loader.check_node():
yield loader.get_node()
finally:
loader.dispose()
def load(stream, Loader=None):
"""
Parse the first YAML document in a stream
and produce the corresponding Python object.
"""
if Loader is None:
load_warning('load')
Loader = FullLoader
loader = Loader(stream)
try:
return loader.get_single_data()
finally:
loader.dispose()
def load_all(stream, Loader=None):
"""
Parse all YAML documents in a stream
and produce corresponding Python objects.
"""
if Loader is None:
load_warning('load_all')
Loader = FullLoader
loader = Loader(stream)
try:
while loader.check_data():
yield loader.get_data()
finally:
loader.dispose()
def full_load(stream):
"""
Parse the first YAML document in a stream
and produce the corresponding Python object.
Resolve all tags except those known to be
unsafe on untrusted input.
"""
return load(stream, FullLoader)
def full_load_all(stream):
"""
Parse all YAML documents in a stream
and produce corresponding Python objects.
Resolve all tags except those known to be
unsafe on untrusted input.
"""
return load_all(stream, FullLoader)
def safe_load(stream):
"""
Parse the first YAML document in a stream
and produce the corresponding Python object.
Resolve only basic YAML tags. This is known
to be safe for untrusted input.
"""
return load(stream, SafeLoader)
def safe_load_all(stream):
"""
Parse all YAML documents in a stream
and produce corresponding Python objects.
Resolve only basic YAML tags. This is known
to be safe for untrusted input.
"""
return load_all(stream, SafeLoader)
def unsafe_load(stream):
"""
Parse the first YAML document in a stream
and produce the corresponding Python object.
Resolve all tags, even those known to be
unsafe on untrusted input.
"""
return load(stream, UnsafeLoader)
def unsafe_load_all(stream):
"""
Parse all YAML documents in a stream
and produce corresponding Python objects.
Resolve all tags, even those known to be
unsafe on untrusted input.
"""
return load_all(stream, UnsafeLoader)
def emit(events, stream=None, Dumper=Dumper,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None):
"""
Emit YAML parsing events into a stream.
If stream is None, return the produced string instead.
"""
getvalue = None
if stream is None:
stream = io.StringIO()
getvalue = stream.getvalue
dumper = Dumper(stream, canonical=canonical, indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break)
try:
for event in events:
dumper.emit(event)
finally:
dumper.dispose()
if getvalue:
return getvalue()
def serialize_all(nodes, stream=None, Dumper=Dumper,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
"""
Serialize a sequence of representation trees into a YAML stream.
If stream is None, return the produced string instead.
"""
getvalue = None
if stream is None:
if encoding is None:
stream = io.StringIO()
else:
stream = io.BytesIO()
getvalue = stream.getvalue
dumper = Dumper(stream, canonical=canonical, indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break,
encoding=encoding, version=version, tags=tags,
explicit_start=explicit_start, explicit_end=explicit_end)
try:
dumper.open()
for node in nodes:
dumper.serialize(node)
dumper.close()
finally:
dumper.dispose()
if getvalue:
return getvalue()
def serialize(node, stream=None, Dumper=Dumper, **kwds):
"""
Serialize a representation tree into a YAML stream.
If stream is None, return the produced string instead.
"""
return serialize_all([node], stream, Dumper=Dumper, **kwds)
def dump_all(documents, stream=None, Dumper=Dumper,
default_style=None, default_flow_style=False,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None, sort_keys=True):
"""
Serialize a sequence of Python objects into a YAML stream.
If stream is None, return the produced string instead.
"""
getvalue = None
if stream is None:
if encoding is None:
stream = io.StringIO()
else:
stream = io.BytesIO()
getvalue = stream.getvalue
dumper = Dumper(stream, default_style=default_style,
default_flow_style=default_flow_style,
canonical=canonical, indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break,
encoding=encoding, version=version, tags=tags,
explicit_start=explicit_start, explicit_end=explicit_end, sort_keys=sort_keys)
try:
dumper.open()
for data in documents:
dumper.represent(data)
dumper.close()
finally:
dumper.dispose()
if getvalue:
return getvalue()
def dump(data, stream=None, Dumper=Dumper, **kwds):
"""
Serialize a Python object into a YAML stream.
If stream is None, return the produced string instead.
"""
return dump_all([data], stream, Dumper=Dumper, **kwds)
def safe_dump_all(documents, stream=None, **kwds):
"""
Serialize a sequence of Python objects into a YAML stream.
Produce only basic YAML tags.
If stream is None, return the produced string instead.
"""
return dump_all(documents, stream, Dumper=SafeDumper, **kwds)
def safe_dump(data, stream=None, **kwds):
"""
Serialize a Python object into a YAML stream.
Produce only basic YAML tags.
If stream is None, return the produced string instead.
"""
return dump_all([data], stream, Dumper=SafeDumper, **kwds)
def add_implicit_resolver(tag, regexp, first=None,
Loader=None, Dumper=Dumper):
"""
Add an implicit scalar detector.
If an implicit scalar value matches the given regexp,
the corresponding tag is assigned to the scalar.
first is a sequence of possible initial characters or None.
"""
if Loader is None:
loader.Loader.add_implicit_resolver(tag, regexp, first)
loader.FullLoader.add_implicit_resolver(tag, regexp, first)
loader.UnsafeLoader.add_implicit_resolver(tag, regexp, first)
else:
Loader.add_implicit_resolver(tag, regexp, first)
Dumper.add_implicit_resolver(tag, regexp, first)
def add_path_resolver(tag, path, kind=None, Loader=None, Dumper=Dumper):
"""
Add a path based resolver for the given tag.
A path is a list of keys that forms a path
to a node in the representation tree.
Keys can be string values, integers, or None.
"""
if Loader is None:
loader.Loader.add_path_resolver(tag, path, kind)
loader.FullLoader.add_path_resolver(tag, path, kind)
loader.UnsafeLoader.add_path_resolver(tag, path, kind)
else:
Loader.add_path_resolver(tag, path, kind)
Dumper.add_path_resolver(tag, path, kind)
def add_constructor(tag, constructor, Loader=None):
"""
Add a constructor for the given tag.
Constructor is a function that accepts a Loader instance
and a node object and produces the corresponding Python object.
"""
if Loader is None:
loader.Loader.add_constructor(tag, constructor)
loader.FullLoader.add_constructor(tag, constructor)
loader.UnsafeLoader.add_constructor(tag, constructor)
else:
Loader.add_constructor(tag, constructor)
def add_multi_constructor(tag_prefix, multi_constructor, Loader=None):
"""
Add a multi-constructor for the given tag prefix.
Multi-constructor is called for a node if its tag starts with tag_prefix.
Multi-constructor accepts a Loader instance, a tag suffix,
and a node object and produces the corresponding Python object.
"""
if Loader is None:
loader.Loader.add_multi_constructor(tag_prefix, multi_constructor)
loader.FullLoader.add_multi_constructor(tag_prefix, multi_constructor)
loader.UnsafeLoader.add_multi_constructor(tag_prefix, multi_constructor)
else:
Loader.add_multi_constructor(tag_prefix, multi_constructor)
def add_representer(data_type, representer, Dumper=Dumper):
"""
Add a representer for the given type.
Representer is a function accepting a Dumper instance
and an instance of the given data type
and producing the corresponding representation node.
"""
Dumper.add_representer(data_type, representer)
def add_multi_representer(data_type, multi_representer, Dumper=Dumper):
"""
Add a representer for the given type.
Multi-representer is a function accepting a Dumper instance
and an instance of the given data type or subtype
and producing the corresponding representation node.
"""
Dumper.add_multi_representer(data_type, multi_representer)
class YAMLObjectMetaclass(type):
"""
The metaclass for YAMLObject.
"""
def __init__(cls, name, bases, kwds):
super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
if isinstance(cls.yaml_loader, list):
for loader in cls.yaml_loader:
loader.add_constructor(cls.yaml_tag, cls.from_yaml)
else:
cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)
cls.yaml_dumper.add_representer(cls, cls.to_yaml)
class YAMLObject(metaclass=YAMLObjectMetaclass):
"""
An object that can dump itself to a YAML stream
and load itself from a YAML stream.
"""
__slots__ = () # no direct instantiation, so allow immutable subclasses
yaml_loader = [Loader, FullLoader, UnsafeLoader]
yaml_dumper = Dumper
yaml_tag = None
yaml_flow_style = None
@classmethod
def from_yaml(cls, loader, node):
"""
Convert a representation node to a Python object.
"""
return loader.construct_yaml_object(node, cls)
@classmethod
def to_yaml(cls, dumper, data):
"""
Convert a Python object to a representation node.
"""
return dumper.represent_yaml_object(cls.yaml_tag, data, cls,
flow_style=cls.yaml_flow_style)

View File

@ -1,139 +0,0 @@
__all__ = ['Composer', 'ComposerError']
from .error import MarkedYAMLError
from .events import *
from .nodes import *
class ComposerError(MarkedYAMLError):
pass
class Composer:
def __init__(self):
self.anchors = {}
def check_node(self):
# Drop the STREAM-START event.
if self.check_event(StreamStartEvent):
self.get_event()
# If there are more documents available?
return not self.check_event(StreamEndEvent)
def get_node(self):
# Get the root node of the next document.
if not self.check_event(StreamEndEvent):
return self.compose_document()
def get_single_node(self):
# Drop the STREAM-START event.
self.get_event()
# Compose a document if the stream is not empty.
document = None
if not self.check_event(StreamEndEvent):
document = self.compose_document()
# Ensure that the stream contains no more documents.
if not self.check_event(StreamEndEvent):
event = self.get_event()
raise ComposerError("expected a single document in the stream",
document.start_mark, "but found another document",
event.start_mark)
# Drop the STREAM-END event.
self.get_event()
return document
def compose_document(self):
# Drop the DOCUMENT-START event.
self.get_event()
# Compose the root node.
node = self.compose_node(None, None)
# Drop the DOCUMENT-END event.
self.get_event()
self.anchors = {}
return node
def compose_node(self, parent, index):
if self.check_event(AliasEvent):
event = self.get_event()
anchor = event.anchor
if anchor not in self.anchors:
raise ComposerError(None, None, "found undefined alias %r"
% anchor, event.start_mark)
return self.anchors[anchor]
event = self.peek_event()
anchor = event.anchor
if anchor is not None:
if anchor in self.anchors:
raise ComposerError("found duplicate anchor %r; first occurrence"
% anchor, self.anchors[anchor].start_mark,
"second occurrence", event.start_mark)
self.descend_resolver(parent, index)
if self.check_event(ScalarEvent):
node = self.compose_scalar_node(anchor)
elif self.check_event(SequenceStartEvent):
node = self.compose_sequence_node(anchor)
elif self.check_event(MappingStartEvent):
node = self.compose_mapping_node(anchor)
self.ascend_resolver()
return node
def compose_scalar_node(self, anchor):
event = self.get_event()
tag = event.tag
if tag is None or tag == '!':
tag = self.resolve(ScalarNode, event.value, event.implicit)
node = ScalarNode(tag, event.value,
event.start_mark, event.end_mark, style=event.style)
if anchor is not None:
self.anchors[anchor] = node
return node
def compose_sequence_node(self, anchor):
start_event = self.get_event()
tag = start_event.tag
if tag is None or tag == '!':
tag = self.resolve(SequenceNode, None, start_event.implicit)
node = SequenceNode(tag, [],
start_event.start_mark, None,
flow_style=start_event.flow_style)
if anchor is not None:
self.anchors[anchor] = node
index = 0
while not self.check_event(SequenceEndEvent):
node.value.append(self.compose_node(node, index))
index += 1
end_event = self.get_event()
node.end_mark = end_event.end_mark
return node
def compose_mapping_node(self, anchor):
start_event = self.get_event()
tag = start_event.tag
if tag is None or tag == '!':
tag = self.resolve(MappingNode, None, start_event.implicit)
node = MappingNode(tag, [],
start_event.start_mark, None,
flow_style=start_event.flow_style)
if anchor is not None:
self.anchors[anchor] = node
while not self.check_event(MappingEndEvent):
#key_event = self.peek_event()
item_key = self.compose_node(node, None)
#if item_key in node.value:
# raise ComposerError("while composing a mapping", start_event.start_mark,
# "found duplicate key", key_event.start_mark)
item_value = self.compose_node(node, item_key)
#node.value[item_key] = item_value
node.value.append((item_key, item_value))
end_event = self.get_event()
node.end_mark = end_event.end_mark
return node

View File

@ -1,748 +0,0 @@
__all__ = [
'BaseConstructor',
'SafeConstructor',
'FullConstructor',
'UnsafeConstructor',
'Constructor',
'ConstructorError'
]
from .error import *
from .nodes import *
import collections.abc, datetime, base64, binascii, re, sys, types
class ConstructorError(MarkedYAMLError):
pass
class BaseConstructor:
yaml_constructors = {}
yaml_multi_constructors = {}
def __init__(self):
self.constructed_objects = {}
self.recursive_objects = {}
self.state_generators = []
self.deep_construct = False
def check_data(self):
# If there are more documents available?
return self.check_node()
def check_state_key(self, key):
"""Block special attributes/methods from being set in a newly created
object, to prevent user-controlled methods from being called during
deserialization"""
if self.get_state_keys_blacklist_regexp().match(key):
raise ConstructorError(None, None,
"blacklisted key '%s' in instance state found" % (key,), None)
def get_data(self):
# Construct and return the next document.
if self.check_node():
return self.construct_document(self.get_node())
def get_single_data(self):
# Ensure that the stream contains a single document and construct it.
node = self.get_single_node()
if node is not None:
return self.construct_document(node)
return None
def construct_document(self, node):
data = self.construct_object(node)
while self.state_generators:
state_generators = self.state_generators
self.state_generators = []
for generator in state_generators:
for dummy in generator:
pass
self.constructed_objects = {}
self.recursive_objects = {}
self.deep_construct = False
return data
def construct_object(self, node, deep=False):
if node in self.constructed_objects:
return self.constructed_objects[node]
if deep:
old_deep = self.deep_construct
self.deep_construct = True
if node in self.recursive_objects:
raise ConstructorError(None, None,
"found unconstructable recursive node", node.start_mark)
self.recursive_objects[node] = None
constructor = None
tag_suffix = None
if node.tag in self.yaml_constructors:
constructor = self.yaml_constructors[node.tag]
else:
for tag_prefix in self.yaml_multi_constructors:
if tag_prefix is not None and node.tag.startswith(tag_prefix):
tag_suffix = node.tag[len(tag_prefix):]
constructor = self.yaml_multi_constructors[tag_prefix]
break
else:
if None in self.yaml_multi_constructors:
tag_suffix = node.tag
constructor = self.yaml_multi_constructors[None]
elif None in self.yaml_constructors:
constructor = self.yaml_constructors[None]
elif isinstance(node, ScalarNode):
constructor = self.__class__.construct_scalar
elif isinstance(node, SequenceNode):
constructor = self.__class__.construct_sequence
elif isinstance(node, MappingNode):
constructor = self.__class__.construct_mapping
if tag_suffix is None:
data = constructor(self, node)
else:
data = constructor(self, tag_suffix, node)
if isinstance(data, types.GeneratorType):
generator = data
data = next(generator)
if self.deep_construct:
for dummy in generator:
pass
else:
self.state_generators.append(generator)
self.constructed_objects[node] = data
del self.recursive_objects[node]
if deep:
self.deep_construct = old_deep
return data
def construct_scalar(self, node):
if not isinstance(node, ScalarNode):
raise ConstructorError(None, None,
"expected a scalar node, but found %s" % node.id,
node.start_mark)
return node.value
def construct_sequence(self, node, deep=False):
if not isinstance(node, SequenceNode):
raise ConstructorError(None, None,
"expected a sequence node, but found %s" % node.id,
node.start_mark)
return [self.construct_object(child, deep=deep)
for child in node.value]
def construct_mapping(self, node, deep=False):
if not isinstance(node, MappingNode):
raise ConstructorError(None, None,
"expected a mapping node, but found %s" % node.id,
node.start_mark)
mapping = {}
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
if not isinstance(key, collections.abc.Hashable):
raise ConstructorError("while constructing a mapping", node.start_mark,
"found unhashable key", key_node.start_mark)
value = self.construct_object(value_node, deep=deep)
mapping[key] = value
return mapping
def construct_pairs(self, node, deep=False):
if not isinstance(node, MappingNode):
raise ConstructorError(None, None,
"expected a mapping node, but found %s" % node.id,
node.start_mark)
pairs = []
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
value = self.construct_object(value_node, deep=deep)
pairs.append((key, value))
return pairs
@classmethod
def add_constructor(cls, tag, constructor):
if not 'yaml_constructors' in cls.__dict__:
cls.yaml_constructors = cls.yaml_constructors.copy()
cls.yaml_constructors[tag] = constructor
@classmethod
def add_multi_constructor(cls, tag_prefix, multi_constructor):
if not 'yaml_multi_constructors' in cls.__dict__:
cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy()
cls.yaml_multi_constructors[tag_prefix] = multi_constructor
class SafeConstructor(BaseConstructor):
def construct_scalar(self, node):
if isinstance(node, MappingNode):
for key_node, value_node in node.value:
if key_node.tag == 'tag:yaml.org,2002:value':
return self.construct_scalar(value_node)
return super().construct_scalar(node)
def flatten_mapping(self, node):
merge = []
index = 0
while index < len(node.value):
key_node, value_node = node.value[index]
if key_node.tag == 'tag:yaml.org,2002:merge':
del node.value[index]
if isinstance(value_node, MappingNode):
self.flatten_mapping(value_node)
merge.extend(value_node.value)
elif isinstance(value_node, SequenceNode):
submerge = []
for subnode in value_node.value:
if not isinstance(subnode, MappingNode):
raise ConstructorError("while constructing a mapping",
node.start_mark,
"expected a mapping for merging, but found %s"
% subnode.id, subnode.start_mark)
self.flatten_mapping(subnode)
submerge.append(subnode.value)
submerge.reverse()
for value in submerge:
merge.extend(value)
else:
raise ConstructorError("while constructing a mapping", node.start_mark,
"expected a mapping or list of mappings for merging, but found %s"
% value_node.id, value_node.start_mark)
elif key_node.tag == 'tag:yaml.org,2002:value':
key_node.tag = 'tag:yaml.org,2002:str'
index += 1
else:
index += 1
if merge:
node.value = merge + node.value
def construct_mapping(self, node, deep=False):
if isinstance(node, MappingNode):
self.flatten_mapping(node)
return super().construct_mapping(node, deep=deep)
def construct_yaml_null(self, node):
self.construct_scalar(node)
return None
bool_values = {
'yes': True,
'no': False,
'true': True,
'false': False,
'on': True,
'off': False,
}
def construct_yaml_bool(self, node):
value = self.construct_scalar(node)
return self.bool_values[value.lower()]
def construct_yaml_int(self, node):
value = self.construct_scalar(node)
value = value.replace('_', '')
sign = +1
if value[0] == '-':
sign = -1
if value[0] in '+-':
value = value[1:]
if value == '0':
return 0
elif value.startswith('0b'):
return sign*int(value[2:], 2)
elif value.startswith('0x'):
return sign*int(value[2:], 16)
elif value[0] == '0':
return sign*int(value, 8)
elif ':' in value:
digits = [int(part) for part in value.split(':')]
digits.reverse()
base = 1
value = 0
for digit in digits:
value += digit*base
base *= 60
return sign*value
else:
return sign*int(value)
inf_value = 1e300
while inf_value != inf_value*inf_value:
inf_value *= inf_value
nan_value = -inf_value/inf_value # Trying to make a quiet NaN (like C99).
def construct_yaml_float(self, node):
value = self.construct_scalar(node)
value = value.replace('_', '').lower()
sign = +1
if value[0] == '-':
sign = -1
if value[0] in '+-':
value = value[1:]
if value == '.inf':
return sign*self.inf_value
elif value == '.nan':
return self.nan_value
elif ':' in value:
digits = [float(part) for part in value.split(':')]
digits.reverse()
base = 1
value = 0.0
for digit in digits:
value += digit*base
base *= 60
return sign*value
else:
return sign*float(value)
def construct_yaml_binary(self, node):
try:
value = self.construct_scalar(node).encode('ascii')
except UnicodeEncodeError as exc:
raise ConstructorError(None, None,
"failed to convert base64 data into ascii: %s" % exc,
node.start_mark)
try:
if hasattr(base64, 'decodebytes'):
return base64.decodebytes(value)
else:
return base64.decodestring(value)
except binascii.Error as exc:
raise ConstructorError(None, None,
"failed to decode base64 data: %s" % exc, node.start_mark)
timestamp_regexp = re.compile(
r'''^(?P<year>[0-9][0-9][0-9][0-9])
-(?P<month>[0-9][0-9]?)
-(?P<day>[0-9][0-9]?)
(?:(?:[Tt]|[ \t]+)
(?P<hour>[0-9][0-9]?)
:(?P<minute>[0-9][0-9])
:(?P<second>[0-9][0-9])
(?:\.(?P<fraction>[0-9]*))?
(?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
(?::(?P<tz_minute>[0-9][0-9]))?))?)?$''', re.X)
def construct_yaml_timestamp(self, node):
value = self.construct_scalar(node)
match = self.timestamp_regexp.match(node.value)
values = match.groupdict()
year = int(values['year'])
month = int(values['month'])
day = int(values['day'])
if not values['hour']:
return datetime.date(year, month, day)
hour = int(values['hour'])
minute = int(values['minute'])
second = int(values['second'])
fraction = 0
tzinfo = None
if values['fraction']:
fraction = values['fraction'][:6]
while len(fraction) < 6:
fraction += '0'
fraction = int(fraction)
if values['tz_sign']:
tz_hour = int(values['tz_hour'])
tz_minute = int(values['tz_minute'] or 0)
delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
if values['tz_sign'] == '-':
delta = -delta
tzinfo = datetime.timezone(delta)
elif values['tz']:
tzinfo = datetime.timezone.utc
return datetime.datetime(year, month, day, hour, minute, second, fraction,
tzinfo=tzinfo)
def construct_yaml_omap(self, node):
# Note: we do not check for duplicate keys, because it's too
# CPU-expensive.
omap = []
yield omap
if not isinstance(node, SequenceNode):
raise ConstructorError("while constructing an ordered map", node.start_mark,
"expected a sequence, but found %s" % node.id, node.start_mark)
for subnode in node.value:
if not isinstance(subnode, MappingNode):
raise ConstructorError("while constructing an ordered map", node.start_mark,
"expected a mapping of length 1, but found %s" % subnode.id,
subnode.start_mark)
if len(subnode.value) != 1:
raise ConstructorError("while constructing an ordered map", node.start_mark,
"expected a single mapping item, but found %d items" % len(subnode.value),
subnode.start_mark)
key_node, value_node = subnode.value[0]
key = self.construct_object(key_node)
value = self.construct_object(value_node)
omap.append((key, value))
def construct_yaml_pairs(self, node):
# Note: the same code as `construct_yaml_omap`.
pairs = []
yield pairs
if not isinstance(node, SequenceNode):
raise ConstructorError("while constructing pairs", node.start_mark,
"expected a sequence, but found %s" % node.id, node.start_mark)
for subnode in node.value:
if not isinstance(subnode, MappingNode):
raise ConstructorError("while constructing pairs", node.start_mark,
"expected a mapping of length 1, but found %s" % subnode.id,
subnode.start_mark)
if len(subnode.value) != 1:
raise ConstructorError("while constructing pairs", node.start_mark,
"expected a single mapping item, but found %d items" % len(subnode.value),
subnode.start_mark)
key_node, value_node = subnode.value[0]
key = self.construct_object(key_node)
value = self.construct_object(value_node)
pairs.append((key, value))
def construct_yaml_set(self, node):
data = set()
yield data
value = self.construct_mapping(node)
data.update(value)
def construct_yaml_str(self, node):
return self.construct_scalar(node)
def construct_yaml_seq(self, node):
data = []
yield data
data.extend(self.construct_sequence(node))
def construct_yaml_map(self, node):
data = {}
yield data
value = self.construct_mapping(node)
data.update(value)
def construct_yaml_object(self, node, cls):
data = cls.__new__(cls)
yield data
if hasattr(data, '__setstate__'):
state = self.construct_mapping(node, deep=True)
data.__setstate__(state)
else:
state = self.construct_mapping(node)
data.__dict__.update(state)
def construct_undefined(self, node):
raise ConstructorError(None, None,
"could not determine a constructor for the tag %r" % node.tag,
node.start_mark)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:null',
SafeConstructor.construct_yaml_null)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:bool',
SafeConstructor.construct_yaml_bool)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:int',
SafeConstructor.construct_yaml_int)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:float',
SafeConstructor.construct_yaml_float)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:binary',
SafeConstructor.construct_yaml_binary)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:timestamp',
SafeConstructor.construct_yaml_timestamp)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:omap',
SafeConstructor.construct_yaml_omap)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:pairs',
SafeConstructor.construct_yaml_pairs)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:set',
SafeConstructor.construct_yaml_set)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:str',
SafeConstructor.construct_yaml_str)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:seq',
SafeConstructor.construct_yaml_seq)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:map',
SafeConstructor.construct_yaml_map)
SafeConstructor.add_constructor(None,
SafeConstructor.construct_undefined)
class FullConstructor(SafeConstructor):
# 'extend' is blacklisted because it is used by
# construct_python_object_apply to add `listitems` to a newly generate
# python instance
def get_state_keys_blacklist(self):
return ['^extend$', '^__.*__$']
def get_state_keys_blacklist_regexp(self):
if not hasattr(self, 'state_keys_blacklist_regexp'):
self.state_keys_blacklist_regexp = re.compile('(' + '|'.join(self.get_state_keys_blacklist()) + ')')
return self.state_keys_blacklist_regexp
def construct_python_str(self, node):
return self.construct_scalar(node)
def construct_python_unicode(self, node):
return self.construct_scalar(node)
def construct_python_bytes(self, node):
try:
value = self.construct_scalar(node).encode('ascii')
except UnicodeEncodeError as exc:
raise ConstructorError(None, None,
"failed to convert base64 data into ascii: %s" % exc,
node.start_mark)
try:
if hasattr(base64, 'decodebytes'):
return base64.decodebytes(value)
else:
return base64.decodestring(value)
except binascii.Error as exc:
raise ConstructorError(None, None,
"failed to decode base64 data: %s" % exc, node.start_mark)
def construct_python_long(self, node):
return self.construct_yaml_int(node)
def construct_python_complex(self, node):
return complex(self.construct_scalar(node))
def construct_python_tuple(self, node):
return tuple(self.construct_sequence(node))
def find_python_module(self, name, mark, unsafe=False):
if not name:
raise ConstructorError("while constructing a Python module", mark,
"expected non-empty name appended to the tag", mark)
if unsafe:
try:
__import__(name)
except ImportError as exc:
raise ConstructorError("while constructing a Python module", mark,
"cannot find module %r (%s)" % (name, exc), mark)
if name not in sys.modules:
raise ConstructorError("while constructing a Python module", mark,
"module %r is not imported" % name, mark)
return sys.modules[name]
def find_python_name(self, name, mark, unsafe=False):
if not name:
raise ConstructorError("while constructing a Python object", mark,
"expected non-empty name appended to the tag", mark)
if '.' in name:
module_name, object_name = name.rsplit('.', 1)
else:
module_name = 'builtins'
object_name = name
if unsafe:
try:
__import__(module_name)
except ImportError as exc:
raise ConstructorError("while constructing a Python object", mark,
"cannot find module %r (%s)" % (module_name, exc), mark)
if module_name not in sys.modules:
raise ConstructorError("while constructing a Python object", mark,
"module %r is not imported" % module_name, mark)
module = sys.modules[module_name]
if not hasattr(module, object_name):
raise ConstructorError("while constructing a Python object", mark,
"cannot find %r in the module %r"
% (object_name, module.__name__), mark)
return getattr(module, object_name)
def construct_python_name(self, suffix, node):
value = self.construct_scalar(node)
if value:
raise ConstructorError("while constructing a Python name", node.start_mark,
"expected the empty value, but found %r" % value, node.start_mark)
return self.find_python_name(suffix, node.start_mark)
def construct_python_module(self, suffix, node):
value = self.construct_scalar(node)
if value:
raise ConstructorError("while constructing a Python module", node.start_mark,
"expected the empty value, but found %r" % value, node.start_mark)
return self.find_python_module(suffix, node.start_mark)
def make_python_instance(self, suffix, node,
args=None, kwds=None, newobj=False, unsafe=False):
if not args:
args = []
if not kwds:
kwds = {}
cls = self.find_python_name(suffix, node.start_mark)
if not (unsafe or isinstance(cls, type)):
raise ConstructorError("while constructing a Python instance", node.start_mark,
"expected a class, but found %r" % type(cls),
node.start_mark)
if newobj and isinstance(cls, type):
return cls.__new__(cls, *args, **kwds)
else:
return cls(*args, **kwds)
def set_python_instance_state(self, instance, state, unsafe=False):
if hasattr(instance, '__setstate__'):
instance.__setstate__(state)
else:
slotstate = {}
if isinstance(state, tuple) and len(state) == 2:
state, slotstate = state
if hasattr(instance, '__dict__'):
if not unsafe and state:
for key in state.keys():
self.check_state_key(key)
instance.__dict__.update(state)
elif state:
slotstate.update(state)
for key, value in slotstate.items():
if not unsafe:
self.check_state_key(key)
setattr(instance, key, value)
def construct_python_object(self, suffix, node):
# Format:
# !!python/object:module.name { ... state ... }
instance = self.make_python_instance(suffix, node, newobj=True)
yield instance
deep = hasattr(instance, '__setstate__')
state = self.construct_mapping(node, deep=deep)
self.set_python_instance_state(instance, state)
def construct_python_object_apply(self, suffix, node, newobj=False):
# Format:
# !!python/object/apply # (or !!python/object/new)
# args: [ ... arguments ... ]
# kwds: { ... keywords ... }
# state: ... state ...
# listitems: [ ... listitems ... ]
# dictitems: { ... dictitems ... }
# or short format:
# !!python/object/apply [ ... arguments ... ]
# The difference between !!python/object/apply and !!python/object/new
# is how an object is created, check make_python_instance for details.
if isinstance(node, SequenceNode):
args = self.construct_sequence(node, deep=True)
kwds = {}
state = {}
listitems = []
dictitems = {}
else:
value = self.construct_mapping(node, deep=True)
args = value.get('args', [])
kwds = value.get('kwds', {})
state = value.get('state', {})
listitems = value.get('listitems', [])
dictitems = value.get('dictitems', {})
instance = self.make_python_instance(suffix, node, args, kwds, newobj)
if state:
self.set_python_instance_state(instance, state)
if listitems:
instance.extend(listitems)
if dictitems:
for key in dictitems:
instance[key] = dictitems[key]
return instance
def construct_python_object_new(self, suffix, node):
return self.construct_python_object_apply(suffix, node, newobj=True)
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/none',
FullConstructor.construct_yaml_null)
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/bool',
FullConstructor.construct_yaml_bool)
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/str',
FullConstructor.construct_python_str)
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/unicode',
FullConstructor.construct_python_unicode)
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/bytes',
FullConstructor.construct_python_bytes)
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/int',
FullConstructor.construct_yaml_int)
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/long',
FullConstructor.construct_python_long)
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/float',
FullConstructor.construct_yaml_float)
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/complex',
FullConstructor.construct_python_complex)
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/list',
FullConstructor.construct_yaml_seq)
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/tuple',
FullConstructor.construct_python_tuple)
FullConstructor.add_constructor(
'tag:yaml.org,2002:python/dict',
FullConstructor.construct_yaml_map)
FullConstructor.add_multi_constructor(
'tag:yaml.org,2002:python/name:',
FullConstructor.construct_python_name)
FullConstructor.add_multi_constructor(
'tag:yaml.org,2002:python/module:',
FullConstructor.construct_python_module)
FullConstructor.add_multi_constructor(
'tag:yaml.org,2002:python/object:',
FullConstructor.construct_python_object)
FullConstructor.add_multi_constructor(
'tag:yaml.org,2002:python/object/new:',
FullConstructor.construct_python_object_new)
class UnsafeConstructor(FullConstructor):
def find_python_module(self, name, mark):
return super(UnsafeConstructor, self).find_python_module(name, mark, unsafe=True)
def find_python_name(self, name, mark):
return super(UnsafeConstructor, self).find_python_name(name, mark, unsafe=True)
def make_python_instance(self, suffix, node, args=None, kwds=None, newobj=False):
return super(UnsafeConstructor, self).make_python_instance(
suffix, node, args, kwds, newobj, unsafe=True)
def set_python_instance_state(self, instance, state):
return super(UnsafeConstructor, self).set_python_instance_state(
instance, state, unsafe=True)
UnsafeConstructor.add_multi_constructor(
'tag:yaml.org,2002:python/object/apply:',
UnsafeConstructor.construct_python_object_apply)
# Constructor is same as UnsafeConstructor. Need to leave this in place in case
# people have extended it directly.
class Constructor(UnsafeConstructor):
pass

View File

@ -1,101 +0,0 @@
__all__ = [
'CBaseLoader', 'CSafeLoader', 'CFullLoader', 'CUnsafeLoader', 'CLoader',
'CBaseDumper', 'CSafeDumper', 'CDumper'
]
from _yaml import CParser, CEmitter
from .constructor import *
from .serializer import *
from .representer import *
from .resolver import *
class CBaseLoader(CParser, BaseConstructor, BaseResolver):
def __init__(self, stream):
CParser.__init__(self, stream)
BaseConstructor.__init__(self)
BaseResolver.__init__(self)
class CSafeLoader(CParser, SafeConstructor, Resolver):
def __init__(self, stream):
CParser.__init__(self, stream)
SafeConstructor.__init__(self)
Resolver.__init__(self)
class CFullLoader(CParser, FullConstructor, Resolver):
def __init__(self, stream):
CParser.__init__(self, stream)
FullConstructor.__init__(self)
Resolver.__init__(self)
class CUnsafeLoader(CParser, UnsafeConstructor, Resolver):
def __init__(self, stream):
CParser.__init__(self, stream)
UnsafeConstructor.__init__(self)
Resolver.__init__(self)
class CLoader(CParser, Constructor, Resolver):
def __init__(self, stream):
CParser.__init__(self, stream)
Constructor.__init__(self)
Resolver.__init__(self)
class CBaseDumper(CEmitter, BaseRepresenter, BaseResolver):
def __init__(self, stream,
default_style=None, default_flow_style=False,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None, sort_keys=True):
CEmitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width, encoding=encoding,
allow_unicode=allow_unicode, line_break=line_break,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style, sort_keys=sort_keys)
Resolver.__init__(self)
class CSafeDumper(CEmitter, SafeRepresenter, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=False,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None, sort_keys=True):
CEmitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width, encoding=encoding,
allow_unicode=allow_unicode, line_break=line_break,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
SafeRepresenter.__init__(self, default_style=default_style,
default_flow_style=default_flow_style, sort_keys=sort_keys)
Resolver.__init__(self)
class CDumper(CEmitter, Serializer, Representer, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=False,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None, sort_keys=True):
CEmitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width, encoding=encoding,
allow_unicode=allow_unicode, line_break=line_break,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style, sort_keys=sort_keys)
Resolver.__init__(self)

View File

@ -1,62 +0,0 @@
__all__ = ['BaseDumper', 'SafeDumper', 'Dumper']
from .emitter import *
from .serializer import *
from .representer import *
from .resolver import *
class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver):
def __init__(self, stream,
default_style=None, default_flow_style=False,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None, sort_keys=True):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style, sort_keys=sort_keys)
Resolver.__init__(self)
class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=False,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None, sort_keys=True):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
SafeRepresenter.__init__(self, default_style=default_style,
default_flow_style=default_flow_style, sort_keys=sort_keys)
Resolver.__init__(self)
class Dumper(Emitter, Serializer, Representer, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=False,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None, sort_keys=True):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style, sort_keys=sort_keys)
Resolver.__init__(self)

File diff suppressed because it is too large Load Diff

View File

@ -1,75 +0,0 @@
__all__ = ['Mark', 'YAMLError', 'MarkedYAMLError']
class Mark:
def __init__(self, name, index, line, column, buffer, pointer):
self.name = name
self.index = index
self.line = line
self.column = column
self.buffer = buffer
self.pointer = pointer
def get_snippet(self, indent=4, max_length=75):
if self.buffer is None:
return None
head = ''
start = self.pointer
while start > 0 and self.buffer[start-1] not in '\0\r\n\x85\u2028\u2029':
start -= 1
if self.pointer-start > max_length/2-1:
head = ' ... '
start += 5
break
tail = ''
end = self.pointer
while end < len(self.buffer) and self.buffer[end] not in '\0\r\n\x85\u2028\u2029':
end += 1
if end-self.pointer > max_length/2-1:
tail = ' ... '
end -= 5
break
snippet = self.buffer[start:end]
return ' '*indent + head + snippet + tail + '\n' \
+ ' '*(indent+self.pointer-start+len(head)) + '^'
def __str__(self):
snippet = self.get_snippet()
where = " in \"%s\", line %d, column %d" \
% (self.name, self.line+1, self.column+1)
if snippet is not None:
where += ":\n"+snippet
return where
class YAMLError(Exception):
pass
class MarkedYAMLError(YAMLError):
def __init__(self, context=None, context_mark=None,
problem=None, problem_mark=None, note=None):
self.context = context
self.context_mark = context_mark
self.problem = problem
self.problem_mark = problem_mark
self.note = note
def __str__(self):
lines = []
if self.context is not None:
lines.append(self.context)
if self.context_mark is not None \
and (self.problem is None or self.problem_mark is None
or self.context_mark.name != self.problem_mark.name
or self.context_mark.line != self.problem_mark.line
or self.context_mark.column != self.problem_mark.column):
lines.append(str(self.context_mark))
if self.problem is not None:
lines.append(self.problem)
if self.problem_mark is not None:
lines.append(str(self.problem_mark))
if self.note is not None:
lines.append(self.note)
return '\n'.join(lines)

View File

@ -1,86 +0,0 @@
# Abstract classes.
class Event(object):
def __init__(self, start_mark=None, end_mark=None):
self.start_mark = start_mark
self.end_mark = end_mark
def __repr__(self):
attributes = [key for key in ['anchor', 'tag', 'implicit', 'value']
if hasattr(self, key)]
arguments = ', '.join(['%s=%r' % (key, getattr(self, key))
for key in attributes])
return '%s(%s)' % (self.__class__.__name__, arguments)
class NodeEvent(Event):
def __init__(self, anchor, start_mark=None, end_mark=None):
self.anchor = anchor
self.start_mark = start_mark
self.end_mark = end_mark
class CollectionStartEvent(NodeEvent):
def __init__(self, anchor, tag, implicit, start_mark=None, end_mark=None,
flow_style=None):
self.anchor = anchor
self.tag = tag
self.implicit = implicit
self.start_mark = start_mark
self.end_mark = end_mark
self.flow_style = flow_style
class CollectionEndEvent(Event):
pass
# Implementations.
class StreamStartEvent(Event):
def __init__(self, start_mark=None, end_mark=None, encoding=None):
self.start_mark = start_mark
self.end_mark = end_mark
self.encoding = encoding
class StreamEndEvent(Event):
pass
class DocumentStartEvent(Event):
def __init__(self, start_mark=None, end_mark=None,
explicit=None, version=None, tags=None):
self.start_mark = start_mark
self.end_mark = end_mark
self.explicit = explicit
self.version = version
self.tags = tags
class DocumentEndEvent(Event):
def __init__(self, start_mark=None, end_mark=None,
explicit=None):
self.start_mark = start_mark
self.end_mark = end_mark
self.explicit = explicit
class AliasEvent(NodeEvent):
pass
class ScalarEvent(NodeEvent):
def __init__(self, anchor, tag, implicit, value,
start_mark=None, end_mark=None, style=None):
self.anchor = anchor
self.tag = tag
self.implicit = implicit
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
self.style = style
class SequenceStartEvent(CollectionStartEvent):
pass
class SequenceEndEvent(CollectionEndEvent):
pass
class MappingStartEvent(CollectionStartEvent):
pass
class MappingEndEvent(CollectionEndEvent):
pass

View File

@ -1,63 +0,0 @@
__all__ = ['BaseLoader', 'FullLoader', 'SafeLoader', 'Loader', 'UnsafeLoader']
from .reader import *
from .scanner import *
from .parser import *
from .composer import *
from .constructor import *
from .resolver import *
class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
BaseConstructor.__init__(self)
BaseResolver.__init__(self)
class FullLoader(Reader, Scanner, Parser, Composer, FullConstructor, Resolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
FullConstructor.__init__(self)
Resolver.__init__(self)
class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
SafeConstructor.__init__(self)
Resolver.__init__(self)
class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
Constructor.__init__(self)
Resolver.__init__(self)
# UnsafeLoader is the same as Loader (which is and was always unsafe on
# untrusted input). Use of either Loader or UnsafeLoader should be rare, since
# FullLoad should be able to load almost all YAML safely. Loader is left intact
# to ensure backwards compatibility.
class UnsafeLoader(Reader, Scanner, Parser, Composer, Constructor, Resolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
Constructor.__init__(self)
Resolver.__init__(self)

View File

@ -1,49 +0,0 @@
class Node(object):
def __init__(self, tag, value, start_mark, end_mark):
self.tag = tag
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
def __repr__(self):
value = self.value
#if isinstance(value, list):
# if len(value) == 0:
# value = '<empty>'
# elif len(value) == 1:
# value = '<1 item>'
# else:
# value = '<%d items>' % len(value)
#else:
# if len(value) > 75:
# value = repr(value[:70]+u' ... ')
# else:
# value = repr(value)
value = repr(value)
return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value)
class ScalarNode(Node):
id = 'scalar'
def __init__(self, tag, value,
start_mark=None, end_mark=None, style=None):
self.tag = tag
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
self.style = style
class CollectionNode(Node):
def __init__(self, tag, value,
start_mark=None, end_mark=None, flow_style=None):
self.tag = tag
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
self.flow_style = flow_style
class SequenceNode(CollectionNode):
id = 'sequence'
class MappingNode(CollectionNode):
id = 'mapping'

View File

@ -1,589 +0,0 @@
# The following YAML grammar is LL(1) and is parsed by a recursive descent
# parser.
#
# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
# implicit_document ::= block_node DOCUMENT-END*
# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
# block_node_or_indentless_sequence ::=
# ALIAS
# | properties (block_content | indentless_block_sequence)?
# | block_content
# | indentless_block_sequence
# block_node ::= ALIAS
# | properties block_content?
# | block_content
# flow_node ::= ALIAS
# | properties flow_content?
# | flow_content
# properties ::= TAG ANCHOR? | ANCHOR TAG?
# block_content ::= block_collection | flow_collection | SCALAR
# flow_content ::= flow_collection | SCALAR
# block_collection ::= block_sequence | block_mapping
# flow_collection ::= flow_sequence | flow_mapping
# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
# indentless_sequence ::= (BLOCK-ENTRY block_node?)+
# block_mapping ::= BLOCK-MAPPING_START
# ((KEY block_node_or_indentless_sequence?)?
# (VALUE block_node_or_indentless_sequence?)?)*
# BLOCK-END
# flow_sequence ::= FLOW-SEQUENCE-START
# (flow_sequence_entry FLOW-ENTRY)*
# flow_sequence_entry?
# FLOW-SEQUENCE-END
# flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
# flow_mapping ::= FLOW-MAPPING-START
# (flow_mapping_entry FLOW-ENTRY)*
# flow_mapping_entry?
# FLOW-MAPPING-END
# flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
#
# FIRST sets:
#
# stream: { STREAM-START }
# explicit_document: { DIRECTIVE DOCUMENT-START }
# implicit_document: FIRST(block_node)
# block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START }
# flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START }
# block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR }
# flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR }
# block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START }
# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START }
# block_sequence: { BLOCK-SEQUENCE-START }
# block_mapping: { BLOCK-MAPPING-START }
# block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START BLOCK-ENTRY }
# indentless_sequence: { ENTRY }
# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START }
# flow_sequence: { FLOW-SEQUENCE-START }
# flow_mapping: { FLOW-MAPPING-START }
# flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY }
# flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY }
__all__ = ['Parser', 'ParserError']
from .error import MarkedYAMLError
from .tokens import *
from .events import *
from .scanner import *
class ParserError(MarkedYAMLError):
pass
class Parser:
# Since writing a recursive-descendant parser is a straightforward task, we
# do not give many comments here.
DEFAULT_TAGS = {
'!': '!',
'!!': 'tag:yaml.org,2002:',
}
def __init__(self):
self.current_event = None
self.yaml_version = None
self.tag_handles = {}
self.states = []
self.marks = []
self.state = self.parse_stream_start
def dispose(self):
# Reset the state attributes (to clear self-references)
self.states = []
self.state = None
def check_event(self, *choices):
# Check the type of the next event.
if self.current_event is None:
if self.state:
self.current_event = self.state()
if self.current_event is not None:
if not choices:
return True
for choice in choices:
if isinstance(self.current_event, choice):
return True
return False
def peek_event(self):
# Get the next event.
if self.current_event is None:
if self.state:
self.current_event = self.state()
return self.current_event
def get_event(self):
# Get the next event and proceed further.
if self.current_event is None:
if self.state:
self.current_event = self.state()
value = self.current_event
self.current_event = None
return value
# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
# implicit_document ::= block_node DOCUMENT-END*
# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
def parse_stream_start(self):
# Parse the stream start.
token = self.get_token()
event = StreamStartEvent(token.start_mark, token.end_mark,
encoding=token.encoding)
# Prepare the next state.
self.state = self.parse_implicit_document_start
return event
def parse_implicit_document_start(self):
# Parse an implicit document.
if not self.check_token(DirectiveToken, DocumentStartToken,
StreamEndToken):
self.tag_handles = self.DEFAULT_TAGS
token = self.peek_token()
start_mark = end_mark = token.start_mark
event = DocumentStartEvent(start_mark, end_mark,
explicit=False)
# Prepare the next state.
self.states.append(self.parse_document_end)
self.state = self.parse_block_node
return event
else:
return self.parse_document_start()
def parse_document_start(self):
# Parse any extra document end indicators.
while self.check_token(DocumentEndToken):
self.get_token()
# Parse an explicit document.
if not self.check_token(StreamEndToken):
token = self.peek_token()
start_mark = token.start_mark
version, tags = self.process_directives()
if not self.check_token(DocumentStartToken):
raise ParserError(None, None,
"expected '<document start>', but found %r"
% self.peek_token().id,
self.peek_token().start_mark)
token = self.get_token()
end_mark = token.end_mark
event = DocumentStartEvent(start_mark, end_mark,
explicit=True, version=version, tags=tags)
self.states.append(self.parse_document_end)
self.state = self.parse_document_content
else:
# Parse the end of the stream.
token = self.get_token()
event = StreamEndEvent(token.start_mark, token.end_mark)
assert not self.states
assert not self.marks
self.state = None
return event
def parse_document_end(self):
# Parse the document end.
token = self.peek_token()
start_mark = end_mark = token.start_mark
explicit = False
if self.check_token(DocumentEndToken):
token = self.get_token()
end_mark = token.end_mark
explicit = True
event = DocumentEndEvent(start_mark, end_mark,
explicit=explicit)
# Prepare the next state.
self.state = self.parse_document_start
return event
def parse_document_content(self):
if self.check_token(DirectiveToken,
DocumentStartToken, DocumentEndToken, StreamEndToken):
event = self.process_empty_scalar(self.peek_token().start_mark)
self.state = self.states.pop()
return event
else:
return self.parse_block_node()
def process_directives(self):
self.yaml_version = None
self.tag_handles = {}
while self.check_token(DirectiveToken):
token = self.get_token()
if token.name == 'YAML':
if self.yaml_version is not None:
raise ParserError(None, None,
"found duplicate YAML directive", token.start_mark)
major, minor = token.value
if major != 1:
raise ParserError(None, None,
"found incompatible YAML document (version 1.* is required)",
token.start_mark)
self.yaml_version = token.value
elif token.name == 'TAG':
handle, prefix = token.value
if handle in self.tag_handles:
raise ParserError(None, None,
"duplicate tag handle %r" % handle,
token.start_mark)
self.tag_handles[handle] = prefix
if self.tag_handles:
value = self.yaml_version, self.tag_handles.copy()
else:
value = self.yaml_version, None
for key in self.DEFAULT_TAGS:
if key not in self.tag_handles:
self.tag_handles[key] = self.DEFAULT_TAGS[key]
return value
# block_node_or_indentless_sequence ::= ALIAS
# | properties (block_content | indentless_block_sequence)?
# | block_content
# | indentless_block_sequence
# block_node ::= ALIAS
# | properties block_content?
# | block_content
# flow_node ::= ALIAS
# | properties flow_content?
# | flow_content
# properties ::= TAG ANCHOR? | ANCHOR TAG?
# block_content ::= block_collection | flow_collection | SCALAR
# flow_content ::= flow_collection | SCALAR
# block_collection ::= block_sequence | block_mapping
# flow_collection ::= flow_sequence | flow_mapping
def parse_block_node(self):
return self.parse_node(block=True)
def parse_flow_node(self):
return self.parse_node()
def parse_block_node_or_indentless_sequence(self):
return self.parse_node(block=True, indentless_sequence=True)
def parse_node(self, block=False, indentless_sequence=False):
if self.check_token(AliasToken):
token = self.get_token()
event = AliasEvent(token.value, token.start_mark, token.end_mark)
self.state = self.states.pop()
else:
anchor = None
tag = None
start_mark = end_mark = tag_mark = None
if self.check_token(AnchorToken):
token = self.get_token()
start_mark = token.start_mark
end_mark = token.end_mark
anchor = token.value
if self.check_token(TagToken):
token = self.get_token()
tag_mark = token.start_mark
end_mark = token.end_mark
tag = token.value
elif self.check_token(TagToken):
token = self.get_token()
start_mark = tag_mark = token.start_mark
end_mark = token.end_mark
tag = token.value
if self.check_token(AnchorToken):
token = self.get_token()
end_mark = token.end_mark
anchor = token.value
if tag is not None:
handle, suffix = tag
if handle is not None:
if handle not in self.tag_handles:
raise ParserError("while parsing a node", start_mark,
"found undefined tag handle %r" % handle,
tag_mark)
tag = self.tag_handles[handle]+suffix
else:
tag = suffix
#if tag == '!':
# raise ParserError("while parsing a node", start_mark,
# "found non-specific tag '!'", tag_mark,
# "Please check 'http://pyyaml.org/wiki/YAMLNonSpecificTag' and share your opinion.")
if start_mark is None:
start_mark = end_mark = self.peek_token().start_mark
event = None
implicit = (tag is None or tag == '!')
if indentless_sequence and self.check_token(BlockEntryToken):
end_mark = self.peek_token().end_mark
event = SequenceStartEvent(anchor, tag, implicit,
start_mark, end_mark)
self.state = self.parse_indentless_sequence_entry
else:
if self.check_token(ScalarToken):
token = self.get_token()
end_mark = token.end_mark
if (token.plain and tag is None) or tag == '!':
implicit = (True, False)
elif tag is None:
implicit = (False, True)
else:
implicit = (False, False)
event = ScalarEvent(anchor, tag, implicit, token.value,
start_mark, end_mark, style=token.style)
self.state = self.states.pop()
elif self.check_token(FlowSequenceStartToken):
end_mark = self.peek_token().end_mark
event = SequenceStartEvent(anchor, tag, implicit,
start_mark, end_mark, flow_style=True)
self.state = self.parse_flow_sequence_first_entry
elif self.check_token(FlowMappingStartToken):
end_mark = self.peek_token().end_mark
event = MappingStartEvent(anchor, tag, implicit,
start_mark, end_mark, flow_style=True)
self.state = self.parse_flow_mapping_first_key
elif block and self.check_token(BlockSequenceStartToken):
end_mark = self.peek_token().start_mark
event = SequenceStartEvent(anchor, tag, implicit,
start_mark, end_mark, flow_style=False)
self.state = self.parse_block_sequence_first_entry
elif block and self.check_token(BlockMappingStartToken):
end_mark = self.peek_token().start_mark
event = MappingStartEvent(anchor, tag, implicit,
start_mark, end_mark, flow_style=False)
self.state = self.parse_block_mapping_first_key
elif anchor is not None or tag is not None:
# Empty scalars are allowed even if a tag or an anchor is
# specified.
event = ScalarEvent(anchor, tag, (implicit, False), '',
start_mark, end_mark)
self.state = self.states.pop()
else:
if block:
node = 'block'
else:
node = 'flow'
token = self.peek_token()
raise ParserError("while parsing a %s node" % node, start_mark,
"expected the node content, but found %r" % token.id,
token.start_mark)
return event
# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
def parse_block_sequence_first_entry(self):
token = self.get_token()
self.marks.append(token.start_mark)
return self.parse_block_sequence_entry()
def parse_block_sequence_entry(self):
if self.check_token(BlockEntryToken):
token = self.get_token()
if not self.check_token(BlockEntryToken, BlockEndToken):
self.states.append(self.parse_block_sequence_entry)
return self.parse_block_node()
else:
self.state = self.parse_block_sequence_entry
return self.process_empty_scalar(token.end_mark)
if not self.check_token(BlockEndToken):
token = self.peek_token()
raise ParserError("while parsing a block collection", self.marks[-1],
"expected <block end>, but found %r" % token.id, token.start_mark)
token = self.get_token()
event = SequenceEndEvent(token.start_mark, token.end_mark)
self.state = self.states.pop()
self.marks.pop()
return event
# indentless_sequence ::= (BLOCK-ENTRY block_node?)+
def parse_indentless_sequence_entry(self):
if self.check_token(BlockEntryToken):
token = self.get_token()
if not self.check_token(BlockEntryToken,
KeyToken, ValueToken, BlockEndToken):
self.states.append(self.parse_indentless_sequence_entry)
return self.parse_block_node()
else:
self.state = self.parse_indentless_sequence_entry
return self.process_empty_scalar(token.end_mark)
token = self.peek_token()
event = SequenceEndEvent(token.start_mark, token.start_mark)
self.state = self.states.pop()
return event
# block_mapping ::= BLOCK-MAPPING_START
# ((KEY block_node_or_indentless_sequence?)?
# (VALUE block_node_or_indentless_sequence?)?)*
# BLOCK-END
def parse_block_mapping_first_key(self):
token = self.get_token()
self.marks.append(token.start_mark)
return self.parse_block_mapping_key()
def parse_block_mapping_key(self):
if self.check_token(KeyToken):
token = self.get_token()
if not self.check_token(KeyToken, ValueToken, BlockEndToken):
self.states.append(self.parse_block_mapping_value)
return self.parse_block_node_or_indentless_sequence()
else:
self.state = self.parse_block_mapping_value
return self.process_empty_scalar(token.end_mark)
if not self.check_token(BlockEndToken):
token = self.peek_token()
raise ParserError("while parsing a block mapping", self.marks[-1],
"expected <block end>, but found %r" % token.id, token.start_mark)
token = self.get_token()
event = MappingEndEvent(token.start_mark, token.end_mark)
self.state = self.states.pop()
self.marks.pop()
return event
def parse_block_mapping_value(self):
if self.check_token(ValueToken):
token = self.get_token()
if not self.check_token(KeyToken, ValueToken, BlockEndToken):
self.states.append(self.parse_block_mapping_key)
return self.parse_block_node_or_indentless_sequence()
else:
self.state = self.parse_block_mapping_key
return self.process_empty_scalar(token.end_mark)
else:
self.state = self.parse_block_mapping_key
token = self.peek_token()
return self.process_empty_scalar(token.start_mark)
# flow_sequence ::= FLOW-SEQUENCE-START
# (flow_sequence_entry FLOW-ENTRY)*
# flow_sequence_entry?
# FLOW-SEQUENCE-END
# flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
#
# Note that while production rules for both flow_sequence_entry and
# flow_mapping_entry are equal, their interpretations are different.
# For `flow_sequence_entry`, the part `KEY flow_node? (VALUE flow_node?)?`
# generate an inline mapping (set syntax).
def parse_flow_sequence_first_entry(self):
token = self.get_token()
self.marks.append(token.start_mark)
return self.parse_flow_sequence_entry(first=True)
def parse_flow_sequence_entry(self, first=False):
if not self.check_token(FlowSequenceEndToken):
if not first:
if self.check_token(FlowEntryToken):
self.get_token()
else:
token = self.peek_token()
raise ParserError("while parsing a flow sequence", self.marks[-1],
"expected ',' or ']', but got %r" % token.id, token.start_mark)
if self.check_token(KeyToken):
token = self.peek_token()
event = MappingStartEvent(None, None, True,
token.start_mark, token.end_mark,
flow_style=True)
self.state = self.parse_flow_sequence_entry_mapping_key
return event
elif not self.check_token(FlowSequenceEndToken):
self.states.append(self.parse_flow_sequence_entry)
return self.parse_flow_node()
token = self.get_token()
event = SequenceEndEvent(token.start_mark, token.end_mark)
self.state = self.states.pop()
self.marks.pop()
return event
def parse_flow_sequence_entry_mapping_key(self):
token = self.get_token()
if not self.check_token(ValueToken,
FlowEntryToken, FlowSequenceEndToken):
self.states.append(self.parse_flow_sequence_entry_mapping_value)
return self.parse_flow_node()
else:
self.state = self.parse_flow_sequence_entry_mapping_value
return self.process_empty_scalar(token.end_mark)
def parse_flow_sequence_entry_mapping_value(self):
if self.check_token(ValueToken):
token = self.get_token()
if not self.check_token(FlowEntryToken, FlowSequenceEndToken):
self.states.append(self.parse_flow_sequence_entry_mapping_end)
return self.parse_flow_node()
else:
self.state = self.parse_flow_sequence_entry_mapping_end
return self.process_empty_scalar(token.end_mark)
else:
self.state = self.parse_flow_sequence_entry_mapping_end
token = self.peek_token()
return self.process_empty_scalar(token.start_mark)
def parse_flow_sequence_entry_mapping_end(self):
self.state = self.parse_flow_sequence_entry
token = self.peek_token()
return MappingEndEvent(token.start_mark, token.start_mark)
# flow_mapping ::= FLOW-MAPPING-START
# (flow_mapping_entry FLOW-ENTRY)*
# flow_mapping_entry?
# FLOW-MAPPING-END
# flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
def parse_flow_mapping_first_key(self):
token = self.get_token()
self.marks.append(token.start_mark)
return self.parse_flow_mapping_key(first=True)
def parse_flow_mapping_key(self, first=False):
if not self.check_token(FlowMappingEndToken):
if not first:
if self.check_token(FlowEntryToken):
self.get_token()
else:
token = self.peek_token()
raise ParserError("while parsing a flow mapping", self.marks[-1],
"expected ',' or '}', but got %r" % token.id, token.start_mark)
if self.check_token(KeyToken):
token = self.get_token()
if not self.check_token(ValueToken,
FlowEntryToken, FlowMappingEndToken):
self.states.append(self.parse_flow_mapping_value)
return self.parse_flow_node()
else:
self.state = self.parse_flow_mapping_value
return self.process_empty_scalar(token.end_mark)
elif not self.check_token(FlowMappingEndToken):
self.states.append(self.parse_flow_mapping_empty_value)
return self.parse_flow_node()
token = self.get_token()
event = MappingEndEvent(token.start_mark, token.end_mark)
self.state = self.states.pop()
self.marks.pop()
return event
def parse_flow_mapping_value(self):
if self.check_token(ValueToken):
token = self.get_token()
if not self.check_token(FlowEntryToken, FlowMappingEndToken):
self.states.append(self.parse_flow_mapping_key)
return self.parse_flow_node()
else:
self.state = self.parse_flow_mapping_key
return self.process_empty_scalar(token.end_mark)
else:
self.state = self.parse_flow_mapping_key
token = self.peek_token()
return self.process_empty_scalar(token.start_mark)
def parse_flow_mapping_empty_value(self):
self.state = self.parse_flow_mapping_key
return self.process_empty_scalar(self.peek_token().start_mark)
def process_empty_scalar(self, mark):
return ScalarEvent(None, None, (True, False), '', mark, mark)

View File

@ -1,185 +0,0 @@
# This module contains abstractions for the input stream. You don't have to
# looks further, there are no pretty code.
#
# We define two classes here.
#
# Mark(source, line, column)
# It's just a record and its only use is producing nice error messages.
# Parser does not use it for any other purposes.
#
# Reader(source, data)
# Reader determines the encoding of `data` and converts it to unicode.
# Reader provides the following methods and attributes:
# reader.peek(length=1) - return the next `length` characters
# reader.forward(length=1) - move the current position to `length` characters.
# reader.index - the number of the current character.
# reader.line, stream.column - the line and the column of the current character.
__all__ = ['Reader', 'ReaderError']
from .error import YAMLError, Mark
import codecs, re
class ReaderError(YAMLError):
def __init__(self, name, position, character, encoding, reason):
self.name = name
self.character = character
self.position = position
self.encoding = encoding
self.reason = reason
def __str__(self):
if isinstance(self.character, bytes):
return "'%s' codec can't decode byte #x%02x: %s\n" \
" in \"%s\", position %d" \
% (self.encoding, ord(self.character), self.reason,
self.name, self.position)
else:
return "unacceptable character #x%04x: %s\n" \
" in \"%s\", position %d" \
% (self.character, self.reason,
self.name, self.position)
class Reader(object):
# Reader:
# - determines the data encoding and converts it to a unicode string,
# - checks if characters are in allowed range,
# - adds '\0' to the end.
# Reader accepts
# - a `bytes` object,
# - a `str` object,
# - a file-like object with its `read` method returning `str`,
# - a file-like object with its `read` method returning `unicode`.
# Yeah, it's ugly and slow.
def __init__(self, stream):
self.name = None
self.stream = None
self.stream_pointer = 0
self.eof = True
self.buffer = ''
self.pointer = 0
self.raw_buffer = None
self.raw_decode = None
self.encoding = None
self.index = 0
self.line = 0
self.column = 0
if isinstance(stream, str):
self.name = "<unicode string>"
self.check_printable(stream)
self.buffer = stream+'\0'
elif isinstance(stream, bytes):
self.name = "<byte string>"
self.raw_buffer = stream
self.determine_encoding()
else:
self.stream = stream
self.name = getattr(stream, 'name', "<file>")
self.eof = False
self.raw_buffer = None
self.determine_encoding()
def peek(self, index=0):
try:
return self.buffer[self.pointer+index]
except IndexError:
self.update(index+1)
return self.buffer[self.pointer+index]
def prefix(self, length=1):
if self.pointer+length >= len(self.buffer):
self.update(length)
return self.buffer[self.pointer:self.pointer+length]
def forward(self, length=1):
if self.pointer+length+1 >= len(self.buffer):
self.update(length+1)
while length:
ch = self.buffer[self.pointer]
self.pointer += 1
self.index += 1
if ch in '\n\x85\u2028\u2029' \
or (ch == '\r' and self.buffer[self.pointer] != '\n'):
self.line += 1
self.column = 0
elif ch != '\uFEFF':
self.column += 1
length -= 1
def get_mark(self):
if self.stream is None:
return Mark(self.name, self.index, self.line, self.column,
self.buffer, self.pointer)
else:
return Mark(self.name, self.index, self.line, self.column,
None, None)
def determine_encoding(self):
while not self.eof and (self.raw_buffer is None or len(self.raw_buffer) < 2):
self.update_raw()
if isinstance(self.raw_buffer, bytes):
if self.raw_buffer.startswith(codecs.BOM_UTF16_LE):
self.raw_decode = codecs.utf_16_le_decode
self.encoding = 'utf-16-le'
elif self.raw_buffer.startswith(codecs.BOM_UTF16_BE):
self.raw_decode = codecs.utf_16_be_decode
self.encoding = 'utf-16-be'
else:
self.raw_decode = codecs.utf_8_decode
self.encoding = 'utf-8'
self.update(1)
NON_PRINTABLE = re.compile('[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD\U00010000-\U0010ffff]')
def check_printable(self, data):
match = self.NON_PRINTABLE.search(data)
if match:
character = match.group()
position = self.index+(len(self.buffer)-self.pointer)+match.start()
raise ReaderError(self.name, position, ord(character),
'unicode', "special characters are not allowed")
def update(self, length):
if self.raw_buffer is None:
return
self.buffer = self.buffer[self.pointer:]
self.pointer = 0
while len(self.buffer) < length:
if not self.eof:
self.update_raw()
if self.raw_decode is not None:
try:
data, converted = self.raw_decode(self.raw_buffer,
'strict', self.eof)
except UnicodeDecodeError as exc:
character = self.raw_buffer[exc.start]
if self.stream is not None:
position = self.stream_pointer-len(self.raw_buffer)+exc.start
else:
position = exc.start
raise ReaderError(self.name, position, character,
exc.encoding, exc.reason)
else:
data = self.raw_buffer
converted = len(data)
self.check_printable(data)
self.buffer += data
self.raw_buffer = self.raw_buffer[converted:]
if self.eof:
self.buffer += '\0'
self.raw_buffer = None
break
def update_raw(self, size=4096):
data = self.stream.read(size)
if self.raw_buffer is None:
self.raw_buffer = data
else:
self.raw_buffer += data
self.stream_pointer += len(data)
if not data:
self.eof = True

View File

@ -1,389 +0,0 @@
__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
'RepresenterError']
from .error import *
from .nodes import *
import datetime, copyreg, types, base64, collections
class RepresenterError(YAMLError):
pass
class BaseRepresenter:
yaml_representers = {}
yaml_multi_representers = {}
def __init__(self, default_style=None, default_flow_style=False, sort_keys=True):
self.default_style = default_style
self.sort_keys = sort_keys
self.default_flow_style = default_flow_style
self.represented_objects = {}
self.object_keeper = []
self.alias_key = None
def represent(self, data):
node = self.represent_data(data)
self.serialize(node)
self.represented_objects = {}
self.object_keeper = []
self.alias_key = None
def represent_data(self, data):
if self.ignore_aliases(data):
self.alias_key = None
else:
self.alias_key = id(data)
if self.alias_key is not None:
if self.alias_key in self.represented_objects:
node = self.represented_objects[self.alias_key]
#if node is None:
# raise RepresenterError("recursive objects are not allowed: %r" % data)
return node
#self.represented_objects[alias_key] = None
self.object_keeper.append(data)
data_types = type(data).__mro__
if data_types[0] in self.yaml_representers:
node = self.yaml_representers[data_types[0]](self, data)
else:
for data_type in data_types:
if data_type in self.yaml_multi_representers:
node = self.yaml_multi_representers[data_type](self, data)
break
else:
if None in self.yaml_multi_representers:
node = self.yaml_multi_representers[None](self, data)
elif None in self.yaml_representers:
node = self.yaml_representers[None](self, data)
else:
node = ScalarNode(None, str(data))
#if alias_key is not None:
# self.represented_objects[alias_key] = node
return node
@classmethod
def add_representer(cls, data_type, representer):
if not 'yaml_representers' in cls.__dict__:
cls.yaml_representers = cls.yaml_representers.copy()
cls.yaml_representers[data_type] = representer
@classmethod
def add_multi_representer(cls, data_type, representer):
if not 'yaml_multi_representers' in cls.__dict__:
cls.yaml_multi_representers = cls.yaml_multi_representers.copy()
cls.yaml_multi_representers[data_type] = representer
def represent_scalar(self, tag, value, style=None):
if style is None:
style = self.default_style
node = ScalarNode(tag, value, style=style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
return node
def represent_sequence(self, tag, sequence, flow_style=None):
value = []
node = SequenceNode(tag, value, flow_style=flow_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
for item in sequence:
node_item = self.represent_data(item)
if not (isinstance(node_item, ScalarNode) and not node_item.style):
best_style = False
value.append(node_item)
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
def represent_mapping(self, tag, mapping, flow_style=None):
value = []
node = MappingNode(tag, value, flow_style=flow_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
if hasattr(mapping, 'items'):
mapping = list(mapping.items())
if self.sort_keys:
try:
mapping = sorted(mapping)
except TypeError:
pass
for item_key, item_value in mapping:
node_key = self.represent_data(item_key)
node_value = self.represent_data(item_value)
if not (isinstance(node_key, ScalarNode) and not node_key.style):
best_style = False
if not (isinstance(node_value, ScalarNode) and not node_value.style):
best_style = False
value.append((node_key, node_value))
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
def ignore_aliases(self, data):
return False
class SafeRepresenter(BaseRepresenter):
def ignore_aliases(self, data):
if data is None:
return True
if isinstance(data, tuple) and data == ():
return True
if isinstance(data, (str, bytes, bool, int, float)):
return True
def represent_none(self, data):
return self.represent_scalar('tag:yaml.org,2002:null', 'null')
def represent_str(self, data):
return self.represent_scalar('tag:yaml.org,2002:str', data)
def represent_binary(self, data):
if hasattr(base64, 'encodebytes'):
data = base64.encodebytes(data).decode('ascii')
else:
data = base64.encodestring(data).decode('ascii')
return self.represent_scalar('tag:yaml.org,2002:binary', data, style='|')
def represent_bool(self, data):
if data:
value = 'true'
else:
value = 'false'
return self.represent_scalar('tag:yaml.org,2002:bool', value)
def represent_int(self, data):
return self.represent_scalar('tag:yaml.org,2002:int', str(data))
inf_value = 1e300
while repr(inf_value) != repr(inf_value*inf_value):
inf_value *= inf_value
def represent_float(self, data):
if data != data or (data == 0.0 and data == 1.0):
value = '.nan'
elif data == self.inf_value:
value = '.inf'
elif data == -self.inf_value:
value = '-.inf'
else:
value = repr(data).lower()
# Note that in some cases `repr(data)` represents a float number
# without the decimal parts. For instance:
# >>> repr(1e17)
# '1e17'
# Unfortunately, this is not a valid float representation according
# to the definition of the `!!float` tag. We fix this by adding
# '.0' before the 'e' symbol.
if '.' not in value and 'e' in value:
value = value.replace('e', '.0e', 1)
return self.represent_scalar('tag:yaml.org,2002:float', value)
def represent_list(self, data):
#pairs = (len(data) > 0 and isinstance(data, list))
#if pairs:
# for item in data:
# if not isinstance(item, tuple) or len(item) != 2:
# pairs = False
# break
#if not pairs:
return self.represent_sequence('tag:yaml.org,2002:seq', data)
#value = []
#for item_key, item_value in data:
# value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
# [(item_key, item_value)]))
#return SequenceNode(u'tag:yaml.org,2002:pairs', value)
def represent_dict(self, data):
return self.represent_mapping('tag:yaml.org,2002:map', data)
def represent_set(self, data):
value = {}
for key in data:
value[key] = None
return self.represent_mapping('tag:yaml.org,2002:set', value)
def represent_date(self, data):
value = data.isoformat()
return self.represent_scalar('tag:yaml.org,2002:timestamp', value)
def represent_datetime(self, data):
value = data.isoformat(' ')
return self.represent_scalar('tag:yaml.org,2002:timestamp', value)
def represent_yaml_object(self, tag, data, cls, flow_style=None):
if hasattr(data, '__getstate__'):
state = data.__getstate__()
else:
state = data.__dict__.copy()
return self.represent_mapping(tag, state, flow_style=flow_style)
def represent_undefined(self, data):
raise RepresenterError("cannot represent an object", data)
SafeRepresenter.add_representer(type(None),
SafeRepresenter.represent_none)
SafeRepresenter.add_representer(str,
SafeRepresenter.represent_str)
SafeRepresenter.add_representer(bytes,
SafeRepresenter.represent_binary)
SafeRepresenter.add_representer(bool,
SafeRepresenter.represent_bool)
SafeRepresenter.add_representer(int,
SafeRepresenter.represent_int)
SafeRepresenter.add_representer(float,
SafeRepresenter.represent_float)
SafeRepresenter.add_representer(list,
SafeRepresenter.represent_list)
SafeRepresenter.add_representer(tuple,
SafeRepresenter.represent_list)
SafeRepresenter.add_representer(dict,
SafeRepresenter.represent_dict)
SafeRepresenter.add_representer(set,
SafeRepresenter.represent_set)
SafeRepresenter.add_representer(datetime.date,
SafeRepresenter.represent_date)
SafeRepresenter.add_representer(datetime.datetime,
SafeRepresenter.represent_datetime)
SafeRepresenter.add_representer(None,
SafeRepresenter.represent_undefined)
class Representer(SafeRepresenter):
def represent_complex(self, data):
if data.imag == 0.0:
data = '%r' % data.real
elif data.real == 0.0:
data = '%rj' % data.imag
elif data.imag > 0:
data = '%r+%rj' % (data.real, data.imag)
else:
data = '%r%rj' % (data.real, data.imag)
return self.represent_scalar('tag:yaml.org,2002:python/complex', data)
def represent_tuple(self, data):
return self.represent_sequence('tag:yaml.org,2002:python/tuple', data)
def represent_name(self, data):
name = '%s.%s' % (data.__module__, data.__name__)
return self.represent_scalar('tag:yaml.org,2002:python/name:'+name, '')
def represent_module(self, data):
return self.represent_scalar(
'tag:yaml.org,2002:python/module:'+data.__name__, '')
def represent_object(self, data):
# We use __reduce__ API to save the data. data.__reduce__ returns
# a tuple of length 2-5:
# (function, args, state, listitems, dictitems)
# For reconstructing, we calls function(*args), then set its state,
# listitems, and dictitems if they are not None.
# A special case is when function.__name__ == '__newobj__'. In this
# case we create the object with args[0].__new__(*args).
# Another special case is when __reduce__ returns a string - we don't
# support it.
# We produce a !!python/object, !!python/object/new or
# !!python/object/apply node.
cls = type(data)
if cls in copyreg.dispatch_table:
reduce = copyreg.dispatch_table[cls](data)
elif hasattr(data, '__reduce_ex__'):
reduce = data.__reduce_ex__(2)
elif hasattr(data, '__reduce__'):
reduce = data.__reduce__()
else:
raise RepresenterError("cannot represent an object", data)
reduce = (list(reduce)+[None]*5)[:5]
function, args, state, listitems, dictitems = reduce
args = list(args)
if state is None:
state = {}
if listitems is not None:
listitems = list(listitems)
if dictitems is not None:
dictitems = dict(dictitems)
if function.__name__ == '__newobj__':
function = args[0]
args = args[1:]
tag = 'tag:yaml.org,2002:python/object/new:'
newobj = True
else:
tag = 'tag:yaml.org,2002:python/object/apply:'
newobj = False
function_name = '%s.%s' % (function.__module__, function.__name__)
if not args and not listitems and not dictitems \
and isinstance(state, dict) and newobj:
return self.represent_mapping(
'tag:yaml.org,2002:python/object:'+function_name, state)
if not listitems and not dictitems \
and isinstance(state, dict) and not state:
return self.represent_sequence(tag+function_name, args)
value = {}
if args:
value['args'] = args
if state or not isinstance(state, dict):
value['state'] = state
if listitems:
value['listitems'] = listitems
if dictitems:
value['dictitems'] = dictitems
return self.represent_mapping(tag+function_name, value)
def represent_ordered_dict(self, data):
# Provide uniform representation across different Python versions.
data_type = type(data)
tag = 'tag:yaml.org,2002:python/object/apply:%s.%s' \
% (data_type.__module__, data_type.__name__)
items = [[key, value] for key, value in data.items()]
return self.represent_sequence(tag, [items])
Representer.add_representer(complex,
Representer.represent_complex)
Representer.add_representer(tuple,
Representer.represent_tuple)
Representer.add_representer(type,
Representer.represent_name)
Representer.add_representer(collections.OrderedDict,
Representer.represent_ordered_dict)
Representer.add_representer(types.FunctionType,
Representer.represent_name)
Representer.add_representer(types.BuiltinFunctionType,
Representer.represent_name)
Representer.add_representer(types.ModuleType,
Representer.represent_module)
Representer.add_multi_representer(object,
Representer.represent_object)

View File

@ -1,227 +0,0 @@
__all__ = ['BaseResolver', 'Resolver']
from .error import *
from .nodes import *
import re
class ResolverError(YAMLError):
pass
class BaseResolver:
DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str'
DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq'
DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:map'
yaml_implicit_resolvers = {}
yaml_path_resolvers = {}
def __init__(self):
self.resolver_exact_paths = []
self.resolver_prefix_paths = []
@classmethod
def add_implicit_resolver(cls, tag, regexp, first):
if not 'yaml_implicit_resolvers' in cls.__dict__:
implicit_resolvers = {}
for key in cls.yaml_implicit_resolvers:
implicit_resolvers[key] = cls.yaml_implicit_resolvers[key][:]
cls.yaml_implicit_resolvers = implicit_resolvers
if first is None:
first = [None]
for ch in first:
cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp))
@classmethod
def add_path_resolver(cls, tag, path, kind=None):
# Note: `add_path_resolver` is experimental. The API could be changed.
# `new_path` is a pattern that is matched against the path from the
# root to the node that is being considered. `node_path` elements are
# tuples `(node_check, index_check)`. `node_check` is a node class:
# `ScalarNode`, `SequenceNode`, `MappingNode` or `None`. `None`
# matches any kind of a node. `index_check` could be `None`, a boolean
# value, a string value, or a number. `None` and `False` match against
# any _value_ of sequence and mapping nodes. `True` matches against
# any _key_ of a mapping node. A string `index_check` matches against
# a mapping value that corresponds to a scalar key which content is
# equal to the `index_check` value. An integer `index_check` matches
# against a sequence value with the index equal to `index_check`.
if not 'yaml_path_resolvers' in cls.__dict__:
cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy()
new_path = []
for element in path:
if isinstance(element, (list, tuple)):
if len(element) == 2:
node_check, index_check = element
elif len(element) == 1:
node_check = element[0]
index_check = True
else:
raise ResolverError("Invalid path element: %s" % element)
else:
node_check = None
index_check = element
if node_check is str:
node_check = ScalarNode
elif node_check is list:
node_check = SequenceNode
elif node_check is dict:
node_check = MappingNode
elif node_check not in [ScalarNode, SequenceNode, MappingNode] \
and not isinstance(node_check, str) \
and node_check is not None:
raise ResolverError("Invalid node checker: %s" % node_check)
if not isinstance(index_check, (str, int)) \
and index_check is not None:
raise ResolverError("Invalid index checker: %s" % index_check)
new_path.append((node_check, index_check))
if kind is str:
kind = ScalarNode
elif kind is list:
kind = SequenceNode
elif kind is dict:
kind = MappingNode
elif kind not in [ScalarNode, SequenceNode, MappingNode] \
and kind is not None:
raise ResolverError("Invalid node kind: %s" % kind)
cls.yaml_path_resolvers[tuple(new_path), kind] = tag
def descend_resolver(self, current_node, current_index):
if not self.yaml_path_resolvers:
return
exact_paths = {}
prefix_paths = []
if current_node:
depth = len(self.resolver_prefix_paths)
for path, kind in self.resolver_prefix_paths[-1]:
if self.check_resolver_prefix(depth, path, kind,
current_node, current_index):
if len(path) > depth:
prefix_paths.append((path, kind))
else:
exact_paths[kind] = self.yaml_path_resolvers[path, kind]
else:
for path, kind in self.yaml_path_resolvers:
if not path:
exact_paths[kind] = self.yaml_path_resolvers[path, kind]
else:
prefix_paths.append((path, kind))
self.resolver_exact_paths.append(exact_paths)
self.resolver_prefix_paths.append(prefix_paths)
def ascend_resolver(self):
if not self.yaml_path_resolvers:
return
self.resolver_exact_paths.pop()
self.resolver_prefix_paths.pop()
def check_resolver_prefix(self, depth, path, kind,
current_node, current_index):
node_check, index_check = path[depth-1]
if isinstance(node_check, str):
if current_node.tag != node_check:
return
elif node_check is not None:
if not isinstance(current_node, node_check):
return
if index_check is True and current_index is not None:
return
if (index_check is False or index_check is None) \
and current_index is None:
return
if isinstance(index_check, str):
if not (isinstance(current_index, ScalarNode)
and index_check == current_index.value):
return
elif isinstance(index_check, int) and not isinstance(index_check, bool):
if index_check != current_index:
return
return True
def resolve(self, kind, value, implicit):
if kind is ScalarNode and implicit[0]:
if value == '':
resolvers = self.yaml_implicit_resolvers.get('', [])
else:
resolvers = self.yaml_implicit_resolvers.get(value[0], [])
resolvers += self.yaml_implicit_resolvers.get(None, [])
for tag, regexp in resolvers:
if regexp.match(value):
return tag
implicit = implicit[1]
if self.yaml_path_resolvers:
exact_paths = self.resolver_exact_paths[-1]
if kind in exact_paths:
return exact_paths[kind]
if None in exact_paths:
return exact_paths[None]
if kind is ScalarNode:
return self.DEFAULT_SCALAR_TAG
elif kind is SequenceNode:
return self.DEFAULT_SEQUENCE_TAG
elif kind is MappingNode:
return self.DEFAULT_MAPPING_TAG
class Resolver(BaseResolver):
pass
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:bool',
re.compile(r'''^(?:yes|Yes|YES|no|No|NO
|true|True|TRUE|false|False|FALSE
|on|On|ON|off|Off|OFF)$''', re.X),
list('yYnNtTfFoO'))
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:float',
re.compile(r'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)?
|\.[0-9_]+(?:[eE][-+][0-9]+)?
|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*
|[-+]?\.(?:inf|Inf|INF)
|\.(?:nan|NaN|NAN))$''', re.X),
list('-+0123456789.'))
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:int',
re.compile(r'''^(?:[-+]?0b[0-1_]+
|[-+]?0[0-7_]+
|[-+]?(?:0|[1-9][0-9_]*)
|[-+]?0x[0-9a-fA-F_]+
|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X),
list('-+0123456789'))
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:merge',
re.compile(r'^(?:<<)$'),
['<'])
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:null',
re.compile(r'''^(?: ~
|null|Null|NULL
| )$''', re.X),
['~', 'n', 'N', ''])
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:timestamp',
re.compile(r'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
|[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]?
(?:[Tt]|[ \t]+)[0-9][0-9]?
:[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)?
(?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X),
list('0123456789'))
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:value',
re.compile(r'^(?:=)$'),
['='])
# The following resolver is only for documentation purposes. It cannot work
# because plain scalars cannot start with '!', '&', or '*'.
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:yaml',
re.compile(r'^(?:!|&|\*)$'),
list('!&*'))

File diff suppressed because it is too large Load Diff

View File

@ -1,111 +0,0 @@
__all__ = ['Serializer', 'SerializerError']
from .error import YAMLError
from .events import *
from .nodes import *
class SerializerError(YAMLError):
pass
class Serializer:
ANCHOR_TEMPLATE = 'id%03d'
def __init__(self, encoding=None,
explicit_start=None, explicit_end=None, version=None, tags=None):
self.use_encoding = encoding
self.use_explicit_start = explicit_start
self.use_explicit_end = explicit_end
self.use_version = version
self.use_tags = tags
self.serialized_nodes = {}
self.anchors = {}
self.last_anchor_id = 0
self.closed = None
def open(self):
if self.closed is None:
self.emit(StreamStartEvent(encoding=self.use_encoding))
self.closed = False
elif self.closed:
raise SerializerError("serializer is closed")
else:
raise SerializerError("serializer is already opened")
def close(self):
if self.closed is None:
raise SerializerError("serializer is not opened")
elif not self.closed:
self.emit(StreamEndEvent())
self.closed = True
#def __del__(self):
# self.close()
def serialize(self, node):
if self.closed is None:
raise SerializerError("serializer is not opened")
elif self.closed:
raise SerializerError("serializer is closed")
self.emit(DocumentStartEvent(explicit=self.use_explicit_start,
version=self.use_version, tags=self.use_tags))
self.anchor_node(node)
self.serialize_node(node, None, None)
self.emit(DocumentEndEvent(explicit=self.use_explicit_end))
self.serialized_nodes = {}
self.anchors = {}
self.last_anchor_id = 0
def anchor_node(self, node):
if node in self.anchors:
if self.anchors[node] is None:
self.anchors[node] = self.generate_anchor(node)
else:
self.anchors[node] = None
if isinstance(node, SequenceNode):
for item in node.value:
self.anchor_node(item)
elif isinstance(node, MappingNode):
for key, value in node.value:
self.anchor_node(key)
self.anchor_node(value)
def generate_anchor(self, node):
self.last_anchor_id += 1
return self.ANCHOR_TEMPLATE % self.last_anchor_id
def serialize_node(self, node, parent, index):
alias = self.anchors[node]
if node in self.serialized_nodes:
self.emit(AliasEvent(alias))
else:
self.serialized_nodes[node] = True
self.descend_resolver(parent, index)
if isinstance(node, ScalarNode):
detected_tag = self.resolve(ScalarNode, node.value, (True, False))
default_tag = self.resolve(ScalarNode, node.value, (False, True))
implicit = (node.tag == detected_tag), (node.tag == default_tag)
self.emit(ScalarEvent(alias, node.tag, implicit, node.value,
style=node.style))
elif isinstance(node, SequenceNode):
implicit = (node.tag
== self.resolve(SequenceNode, node.value, True))
self.emit(SequenceStartEvent(alias, node.tag, implicit,
flow_style=node.flow_style))
index = 0
for item in node.value:
self.serialize_node(item, node, index)
index += 1
self.emit(SequenceEndEvent())
elif isinstance(node, MappingNode):
implicit = (node.tag
== self.resolve(MappingNode, node.value, True))
self.emit(MappingStartEvent(alias, node.tag, implicit,
flow_style=node.flow_style))
for key, value in node.value:
self.serialize_node(key, node, None)
self.serialize_node(value, node, key)
self.emit(MappingEndEvent())
self.ascend_resolver()

View File

@ -1,104 +0,0 @@
class Token(object):
def __init__(self, start_mark, end_mark):
self.start_mark = start_mark
self.end_mark = end_mark
def __repr__(self):
attributes = [key for key in self.__dict__
if not key.endswith('_mark')]
attributes.sort()
arguments = ', '.join(['%s=%r' % (key, getattr(self, key))
for key in attributes])
return '%s(%s)' % (self.__class__.__name__, arguments)
#class BOMToken(Token):
# id = '<byte order mark>'
class DirectiveToken(Token):
id = '<directive>'
def __init__(self, name, value, start_mark, end_mark):
self.name = name
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class DocumentStartToken(Token):
id = '<document start>'
class DocumentEndToken(Token):
id = '<document end>'
class StreamStartToken(Token):
id = '<stream start>'
def __init__(self, start_mark=None, end_mark=None,
encoding=None):
self.start_mark = start_mark
self.end_mark = end_mark
self.encoding = encoding
class StreamEndToken(Token):
id = '<stream end>'
class BlockSequenceStartToken(Token):
id = '<block sequence start>'
class BlockMappingStartToken(Token):
id = '<block mapping start>'
class BlockEndToken(Token):
id = '<block end>'
class FlowSequenceStartToken(Token):
id = '['
class FlowMappingStartToken(Token):
id = '{'
class FlowSequenceEndToken(Token):
id = ']'
class FlowMappingEndToken(Token):
id = '}'
class KeyToken(Token):
id = '?'
class ValueToken(Token):
id = ':'
class BlockEntryToken(Token):
id = '-'
class FlowEntryToken(Token):
id = ','
class AliasToken(Token):
id = '<alias>'
def __init__(self, value, start_mark, end_mark):
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class AnchorToken(Token):
id = '<anchor>'
def __init__(self, value, start_mark, end_mark):
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class TagToken(Token):
id = '<tag>'
def __init__(self, value, start_mark, end_mark):
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class ScalarToken(Token):
id = '<scalar>'
def __init__(self, value, plain, start_mark, end_mark, style=None):
self.value = value
self.plain = plain
self.start_mark = start_mark
self.end_mark = end_mark
self.style = style

49
todo.md
View File

@ -1,49 +0,0 @@
# To-do:
## Support for more connector types
* Inline connectors (IDC)
* Possibly join two logical wires into one physical wire, add up length for BOM creation
* Designators like W1_1, W1_2 or similar to group them?
## Support for more wire types
* Coax cables
* Graphical representation
* Twisted pairs
* Logical representation
* Graphical representation
* Ribbon cables
* Folds
* Splits
* Orientation of IDC connectors
## Support for more links/connections
* Cable splicing
* as pseudo-connector?
* Heatshrink / sheathing
## Visualization
* Parse and render double-colored, striped cables ('RDBU' etc)
* Display picture of connector underneath (including pin 1 location)
## Export
* Export to PDF with frame, title block, ...
## Other
* Imrpove tutorial texts
* Create syntax reference / cheat sheet
* Set global parameters (show_pins, ...) and allow override on per-item basis
* Allow custom GraphViz code before/after WireViz-generated code
* Make "unit tests" for different features/situations
* Missing parameters
* Connection formats
* single wire 1
* multiple wires [1,2,3]
* wire ranges [1-10]
* Loops
* ...

View File

@ -221,7 +221,7 @@ connections:
- # attach ferrules
- F1 # no need for list of connections; one ferrule per wire is auto-generated and attached
- W1: [1-4] # a new ferrule is auto-generated for each wire
- # attach connectors (separetely from ferrules)
- # attach connectors (separately from ferrules)
- W1: [1-4]
- X1: [1-4]
```
@ -262,7 +262,7 @@ ferrules: # ferrules
cables:
W1:
category: bundle # budnle
category: bundle # bundle
length: 0.3
gauge: 0.5 mm2
colors: [YE, BK, BK, RD] # custom colors, wirecount is implicit
@ -271,7 +271,7 @@ connections:
- # attach ferrules
- F_05
- W1: [1,4] # a new ferrule is auto-generated for each wire
- # attach connectors (separetely from ferrules)
- # attach connectors (separately from ferrules)
- W1: [1-4]
- X1: [1-4]
-

View File

@ -21,6 +21,6 @@ connections:
- # attach ferrules
- F1 # no need for list of connections; one ferrule per wire is auto-generated and attached
- W1: [1-4] # a new ferrule is auto-generated for each wire
- # attach connectors (separetely from ferrules)
- # attach connectors (separately from ferrules)
- W1: [1-4]
- X1: [1-4]

View File

@ -17,7 +17,7 @@ ferrules: # ferrules
cables:
W1:
category: bundle # budnle
category: bundle # bundle
length: 0.3
gauge: 0.5 mm2
colors: [YE, BK, BK, RD] # custom colors, wirecount is implicit
@ -26,7 +26,7 @@ connections:
- # attach ferrules
- F_05
- W1: [1,4] # a new ferrule is auto-generated for each wire
- # attach connectors (separetely from ferrules)
- # attach connectors (separately from ferrules)
- W1: [1-4]
- X1: [1-4]
-