a | b/PlcOpenXmlLadderToAsciiLadder.py | ||
---|---|---|---|
1 | # PlcOpenXmlLadderToAsciiLadder |
||
2 | import sys |
||
3 | import xml.dom.minidom |
||
4 | |||
5 | def howCloseAmIToLeftPowerRail(num, ladder, element): |
||
6 | # Function to work out how close an element is to the left... |
||
7 | # element is a dom element of contact, coil, block |
||
8 | # ladder is the whole ladder |
||
9 | # num is the current distance, initially zero |
||
10 | if element.tagName == "contact" or element.tagName == "coil" or element.tagName == "leftPowerRail": |
||
11 | _num = num + 1 |
||
12 | else: # "block", maybe "execute"? |
||
13 | _num = num + 3 |
||
14 | highestnum = _num |
||
15 | for conny in element.getElementsByTagName("connection"): |
||
16 | for _elly in ladder.childNodes: |
||
17 | if _elly.nodeType != _elly.TEXT_NODE: |
||
18 | if _elly.getAttribute("localId") == conny.getAttribute("refLocalId"): |
||
19 | # pass how close this element is |
||
20 | connysNum = howCloseAmIToLeftPowerRail(_num, ladder, _elly) |
||
21 | if connysNum > highestnum: |
||
22 | highestnum = connysNum |
||
23 | return highestnum |
||
24 | |||
25 | # main |
||
26 | if len(sys.argv) == 2: |
||
27 | filepathin = sys.argv[1] |
||
28 | print(filepathin) |
||
29 | doc = xml.dom.minidom.parse(filepathin) |
||
30 | # get the first (currently only) entire declaration header as a string |
||
31 | declarationheader = doc.getElementsByTagName("InterfaceAsPlainText")[0].getElementsByTagName("xhtml")[0].childNodes[0].data |
||
32 | |||
33 | # get the first (currently only) ladder element |
||
34 | ladder = doc.getElementsByTagName("LD")[0] |
||
35 | widthOfLadder = 4 # will grow by itself if the ladder needs it |
||
36 | foundANewLabel = False |
||
37 | networkNumber = 0 |
||
38 | rowId = 0 |
||
39 | |||
40 | gridladder = xml.dom.minidom.Document() |
||
41 | gridladder.appendChild(gridladder.createElement("gridLD")) |
||
42 | |||
43 | for elly in ladder.childNodes: |
||
44 | #Close a branch |
||
45 | # <contact> |
||
46 | # <connectionPointIn> |
||
47 | # <connection refLocalId="39" /> |
||
48 | # <connection refLocalId="40" /> |
||
49 | # </connectionPointIn> |
||
50 | #Open a branch |
||
51 | # <contact > |
||
52 | # <connectionPointIn> |
||
53 | # <connection refLocalId="0" /> |
||
54 | # </connectionPointIn> |
||
55 | # </contact> |
||
56 | # <contact > |
||
57 | # <connectionPointIn> |
||
58 | # <connection refLocalId="0" /> |
||
59 | # </connectionPointIn> |
||
60 | #leftPowerRail |
||
61 | #comment |
||
62 | #label |
||
63 | if elly.nodeName == "label": |
||
64 | print(elly.getAttribute("label")) |
||
65 | # create new <row>, add this label to the zeroth column |
||
66 | networkNumber += 1 |
||
67 | gridladder.firstChild.appendChild(gridladder.createElement("row")) |
||
68 | gridladder.firstChild.lastChild.setAttribute("rowId",str(rowId)) |
||
69 | rowId += 1 |
||
70 | gridladder.firstChild.lastChild.appendChild(gridladder.createElement("Column")) |
||
71 | gridladder.firstChild.lastChild.lastChild.setAttribute("columnId","0") |
||
72 | gridladder.firstChild.lastChild.lastChild.setAttribute("networkId",str(networkNumber)) |
||
73 | gridladder.firstChild.lastChild.lastChild.setAttribute("label",elly.getAttribute("label")) |
||
74 | foundANewLabel = True |
||
75 | #vendorElement (both new network and parallel branches) |
||
76 | if elly.nodeName == "vendorElement": |
||
77 | if elly.getElementsByTagName("ElementType").length > 0: |
||
78 | print("NewNetwork") |
||
79 | if foundANewLabel:# already found a label |
||
80 | foundANewLabel = False |
||
81 | else: |
||
82 | networkNumber += 1 |
||
83 | gridladder.firstChild.appendChild(gridladder.createElement("row")) |
||
84 | gridladder.firstChild.lastChild.setAttribute("rowId",str(rowId)) |
||
85 | rowId += 1 |
||
86 | |||
87 | gridladder.firstChild.lastChild.appendChild(gridladder.createElement("Column")) |
||
88 | gridladder.firstChild.lastChild.lastChild.setAttribute("columnId","0") |
||
89 | gridladder.firstChild.lastChild.lastChild.setAttribute("networkId",str(networkNumber)) |
||
90 | elif elly.getElementsByTagName("ParallelBranch"): |
||
91 | print("New ParallelBranch Short Circuit") |
||
92 | #inVariable (these are your inputs to blocks) |
||
93 | if elly.nodeName == "inVariable": |
||
94 | print("NewInvariable") |
||
95 | #block (FB, F etc) |
||
96 | if elly.nodeName == "block": |
||
97 | print("NewBlock") |
||
98 | mydistance = howCloseAmIToLeftPowerRail(0,ladder,elly) |
||
99 | if mydistance > widthOfLadder: |
||
100 | widthOfLadder = mydistance |
||
101 | |||
102 | if elly.getElementsByTagName("connection")[0].getAttribute("refLocalId") == "0": |
||
103 | # that means it's leftmost object |
||
104 | if gridladder.firstChild.lastChild.lastChild.getAttribute("columnId") != "0": |
||
105 | # that means we parallel branch and need a new row |
||
106 | gridladder.firstChild.appendChild(gridladder.createElement("row")) |
||
107 | gridladder.firstChild.lastChild.setAttribute("rowId",str(rowId)) |
||
108 | rowId += 1 |
||
109 | gridladder.firstChild.lastChild.appendChild(gridladder.createElement("Column")) |
||
110 | gridladder.firstChild.lastChild.lastChild.setAttribute("columnId","1") |
||
111 | gridladder.firstChild.lastChild.lastChild.setAttribute("localId",elly.getAttribute("localId")) |
||
112 | gridladder.firstChild.lastChild.lastChild.setAttribute("width","3") |
||
113 | else: # that means it points behind another object |
||
114 | highestRowId = "65535" |
||
115 | widestColumn = "0" |
||
116 | for ellysUpstream in elly.getElementsByTagName("connection"): |
||
117 | # may be more than one upstream. |
||
118 | # The first downstream will be in the highest row of the upstream and one column back from the furthest back upstream |
||
119 | ellysUpstreamId = ellysUpstream.getAttribute("refLocalId") |
||
120 | for gridellys in gridladder.firstChild.getElementsByTagName("Column"): |
||
121 | if gridellys.getAttribute("localId") == ellysUpstreamId: |
||
122 | if highestRowId > gridellys.parentNode.getAttribute("rowId"): |
||
123 | highestRowId = gridellys.parentNode.getAttribute("rowId") |
||
124 | highestRow = gridellys.parentNode |
||
125 | widestColumn = max(widestColumn, str(int(gridellys.getAttribute("columnId")) + int(gridellys.getAttribute("width")))) |
||
126 | # Is there anyone at that RowColumn? |
||
127 | someoneIsThereAlready = False |
||
128 | for existingColumns in highestRow.getElementsByTagName("Column"): |
||
129 | if existingColumns.getAttribute("columnId") >= widestColumn: |
||
130 | someoneIsThereAlready = True |
||
131 | if not someoneIsThereAlready : |
||
132 | highestRow.appendChild(gridladder.createElement("Column")) |
||
133 | highestRow.lastChild.setAttribute("columnId",widestColumn) |
||
134 | highestRow.lastChild.setAttribute("localId",elly.getAttribute("localId")) |
||
135 | highestRow.lastChild.setAttribute("width","3") |
||
136 | # TODO Do something else if someone is already there |
||
137 | # TODO You will have to flag that it is parallel |
||
138 | #contact |
||
139 | if elly.nodeName == "contact": |
||
140 | if elly.getAttribute("negated") == "true": |
||
141 | print("NewNegatedContact") |
||
142 | else: |
||
143 | print("NewContact") |
||
144 | |||
145 | mydistance = howCloseAmIToLeftPowerRail(0,ladder,elly) |
||
146 | if mydistance > widthOfLadder: |
||
147 | widthOfLadder = mydistance |
||
148 | |||
149 | if elly.getElementsByTagName("connection")[0].getAttribute("refLocalId") == "0": |
||
150 | # that means it's leftmost object |
||
151 | if gridladder.firstChild.lastChild.lastChild.getAttribute("columnId") != "0": |
||
152 | # that means we parallel branch and need a new row |
||
153 | gridladder.firstChild.appendChild(gridladder.createElement("row")) |
||
154 | gridladder.firstChild.lastChild.setAttribute("rowId",str(rowId)) |
||
155 | rowId += 1 |
||
156 | gridladder.firstChild.lastChild.appendChild(gridladder.createElement("Column")) |
||
157 | gridladder.firstChild.lastChild.lastChild.setAttribute("columnId","1") |
||
158 | gridladder.firstChild.lastChild.lastChild.setAttribute("localId",elly.getAttribute("localId")) |
||
159 | gridladder.firstChild.lastChild.lastChild.setAttribute("width","1") |
||
160 | else: # that means it points behind another object |
||
161 | highestRowId = "65535" |
||
162 | widestColumn = "0" |
||
163 | for ellysUpstream in elly.getElementsByTagName("connection"): |
||
164 | # may be more than one upstream. |
||
165 | # The first downstream will be in the highest row of the upstream and one column back from the furthest back upstream |
||
166 | ellysUpstreamId = ellysUpstream.getAttribute("refLocalId") |
||
167 | for gridellys in gridladder.firstChild.getElementsByTagName("Column"): |
||
168 | if gridellys.getAttribute("localId") == ellysUpstreamId: |
||
169 | if highestRowId > gridellys.parentNode.getAttribute("rowId"): |
||
170 | highestRowId = gridellys.parentNode.getAttribute("rowId") |
||
171 | highestRow = gridellys.parentNode |
||
172 | widestColumn = max(widestColumn, str(int(gridellys.getAttribute("columnId")) + int(gridellys.getAttribute("width")))) |
||
173 | # Is there anyone at that RowColumn? |
||
174 | someoneIsThereAlready = False |
||
175 | for existingColumns in highestRow.getElementsByTagName("Column"): |
||
176 | if existingColumns.getAttribute("columnId") >= widestColumn: |
||
177 | someoneIsThereAlready = True |
||
178 | if not someoneIsThereAlready : |
||
179 | highestRow.appendChild(gridladder.createElement("Column")) |
||
180 | highestRow.lastChild.setAttribute("columnId",widestColumn) |
||
181 | highestRow.lastChild.setAttribute("localId",elly.getAttribute("localId")) |
||
182 | highestRow.lastChild.setAttribute("width","1") |
||
183 | # TODO Do something else if someone is already there |
||
184 | # TODO You will have to flag that it is parallel |
||
185 | |||
186 | |||
187 | |||
188 | #coil |
||
189 | if elly.nodeName == "coil": |
||
190 | if elly.getAttribute("storage") == "none": |
||
191 | print("NewCoil") |
||
192 | elif elly.getAttribute("storage") == "set": |
||
193 | print("NewSetCoil") |
||
194 | elif elly.getAttribute("storage") == "reset": |
||
195 | print("NewResetCoil") |
||
196 | |||
197 | mydistance = howCloseAmIToLeftPowerRail(0,ladder,elly) |
||
198 | if mydistance > widthOfLadder: |
||
199 | widthOfLadder = mydistance |
||
200 | |||
201 | if elly.getElementsByTagName("connection")[0].getAttribute("refLocalId") == "0": |
||
202 | # that means it's leftmost object |
||
203 | if gridladder.firstChild.lastChild.lastChild.getAttribute("columnId") != "0": |
||
204 | # that means we parallel branch and need a new row |
||
205 | gridladder.firstChild.appendChild(gridladder.createElement("row")) |
||
206 | gridladder.firstChild.lastChild.setAttribute("rowId",str(rowId)) |
||
207 | rowId += 1 |
||
208 | gridladder.firstChild.lastChild.appendChild(gridladder.createElement("Column")) |
||
209 | gridladder.firstChild.lastChild.lastChild.setAttribute("columnId","1") |
||
210 | gridladder.firstChild.lastChild.lastChild.setAttribute("localId",elly.getAttribute("localId")) |
||
211 | gridladder.firstChild.lastChild.lastChild.setAttribute("width","1") |
||
212 | else: # that means it points behind another object |
||
213 | highestRowId = "65535" |
||
214 | widestColumn = "0" |
||
215 | for ellysUpstream in elly.getElementsByTagName("connection"): |
||
216 | # may be more than one upstream. |
||
217 | # The first downstream will be in the highest row of the upstream and one column back from the furthest back upstream |
||
218 | ellysUpstreamId = ellysUpstream.getAttribute("refLocalId") |
||
219 | for gridellys in gridladder.firstChild.getElementsByTagName("Column"): |
||
220 | if gridellys.getAttribute("localId") == ellysUpstreamId: |
||
221 | if highestRowId > gridellys.parentNode.getAttribute("rowId"): |
||
222 | highestRowId = gridellys.parentNode.getAttribute("rowId") |
||
223 | highestRow = gridellys.parentNode |
||
224 | widestColumn = max(widestColumn, str(int(gridellys.getAttribute("columnId")) + int(gridellys.getAttribute("width")))) |
||
225 | # Is there anyone at that RowColumn? |
||
226 | someoneIsThereAlready = False |
||
227 | for existingColumns in highestRow.getElementsByTagName("Column"): |
||
228 | if existingColumns.getAttribute("columnId") >= widestColumn: |
||
229 | someoneIsThereAlready = True |
||
230 | if not someoneIsThereAlready : |
||
231 | highestRow.appendChild(gridladder.createElement("Column")) |
||
232 | highestRow.lastChild.setAttribute("columnId",widestColumn) |
||
233 | highestRow.lastChild.setAttribute("localId",elly.getAttribute("localId")) |
||
234 | highestRow.lastChild.setAttribute("width","1") |
||
235 | # TODO Do something else if someone is already there |
||
236 | # TODO You will have to flag that it is parallel |
||
237 | |||
238 | #rightPowerRail (think CODESYS were having a laugh) |
||
239 | # How wide to make network? well: |
||
240 | # 1. contacts and coils = 1 |
||
241 | # 2. blocks and executes = 3 |
||
242 | # 3. branches themselves don't take up additional room |
||
243 | # 4. but... you have to work out the longest path on each branch |
||
244 | # Data is presented localId + refLocalId [+ refLocalId + ...] |
||
245 | # So I guess it is easiest to work our way backwards. (from right to left) |
||
246 | # We will naïvely look at every localId and how long it takes to get back to refLocalId=0 |
||
247 | # We also naïvely look at every possible localId, even if we've already found him |
||
248 | # We've actually already calculated that above, yay for me! |
||
249 | print(widthOfLadder) |
||
250 | |||
251 | # Array of networks |
||
252 | # Network is an array of Contact / Block / Coil |
||
253 | # So we know it is (widthOfLadder) wide... so we could have an array.. no that's not right |
||
254 | # 1. Pass through every element |
||
255 | # 2. hmm, lets make a new ladder xml, which is the rows... input variables and labels can be added last |
||
256 | # 3. <Row> |
||
257 | # <Column columnId="0" refLocalId=(localID of vendorElement new network) /> |
||
258 | # <Column columnId="1" refLocalId=(localID of next contact/block/coil) /> |
||
259 | # 4. The next element comes in. It asks if anybody already has the row right behind its refLocalId |
||
260 | # If someone is already there, it starts a new Row, starting at the columnId just behind |
||
261 | # 5. If it has two refLocalIds, it tries to get the furthest back column |
||
262 | print (str(gridladder.toprettyxml())) |
||
263 | |||
264 | # should now have a declaration section |
||
265 | # a bunch of inputs |
||
266 | # a bunch of outputs |
||
267 | # and a bunch of blocks |
||
268 | # convert them all to a nice easy format |
||
269 | print ("{Declaration}") |
||
270 | print (declarationheader) |
||
271 | print ("{/Declaration}") |
||
272 | else: |
||
273 | print("Maybe I want one argument, whch is the PLCOPEN XML.") |
||
274 | print("Will output to current directory as Ladder0.ladder") |
||
275 |