[e7539c]: / PlcOpenXmlLadderToAsciiLadder.py  Maximize  Restore  History

Download this file

276 lines (257 with data), 15.6 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# PlcOpenXmlLadderToAsciiLadder
import sys
import xml.dom.minidom

def howCloseAmIToLeftPowerRail(num, ladder, element):
    # Function to work out how close an element is to the left...
    # element is a dom element of contact, coil, block
    # ladder is the whole ladder
    # num is the current distance, initially zero
    if element.tagName == "contact" or element.tagName == "coil" or element.tagName == "leftPowerRail":
        _num = num + 1
    else: # "block", maybe "execute"?
        _num = num + 3
    highestnum = _num
    for conny in element.getElementsByTagName("connection"):
        for _elly in ladder.childNodes:
            if _elly.nodeType != _elly.TEXT_NODE:
                if _elly.getAttribute("localId") == conny.getAttribute("refLocalId"):
                    # pass how close this element is
                    connysNum = howCloseAmIToLeftPowerRail(_num, ladder, _elly)
        if connysNum > highestnum:
            highestnum = connysNum
    return highestnum

# main
if len(sys.argv) == 2:
    filepathin = sys.argv[1]
    print(filepathin)
    doc = xml.dom.minidom.parse(filepathin)
    # get the first (currently only) entire declaration header as a string
    declarationheader = doc.getElementsByTagName("InterfaceAsPlainText")[0].getElementsByTagName("xhtml")[0].childNodes[0].data
    
    # get the first (currently only) ladder element
    ladder = doc.getElementsByTagName("LD")[0]
    widthOfLadder = 4 # will grow by itself if the ladder needs it
    foundANewLabel = False
    networkNumber = 0
    rowId = 0
  
    gridladder = xml.dom.minidom.Document()
    gridladder.appendChild(gridladder.createElement("gridLD"))
  
    for elly in ladder.childNodes:
        #Close a branch
        #     <contact>
        #        <connectionPointIn>
        #          <connection refLocalId="39" />
        #          <connection refLocalId="40" />
        #        </connectionPointIn>
        #Open a branch
        #      <contact >        
        #        <connectionPointIn>
        #          <connection refLocalId="0" />
        #        </connectionPointIn>
        #      </contact>
        #      <contact >
        #        <connectionPointIn>
        #          <connection refLocalId="0" />
        #        </connectionPointIn>
        #leftPowerRail
        #comment
        #label
        if elly.nodeName == "label":
            print(elly.getAttribute("label"))
            # create new <row>, add this label to the zeroth column
            networkNumber += 1
            gridladder.firstChild.appendChild(gridladder.createElement("row"))
            gridladder.firstChild.lastChild.setAttribute("rowId",str(rowId))
            rowId += 1
            gridladder.firstChild.lastChild.appendChild(gridladder.createElement("Column"))
            gridladder.firstChild.lastChild.lastChild.setAttribute("columnId","0")
            gridladder.firstChild.lastChild.lastChild.setAttribute("networkId",str(networkNumber))
            gridladder.firstChild.lastChild.lastChild.setAttribute("label",elly.getAttribute("label"))
            foundANewLabel = True
        #vendorElement  (both new network and parallel branches)
        if elly.nodeName == "vendorElement":
            if elly.getElementsByTagName("ElementType").length > 0:
                print("NewNetwork")
                if foundANewLabel:# already found a label
                    foundANewLabel = False
                else:
                    networkNumber += 1
                    gridladder.firstChild.appendChild(gridladder.createElement("row"))
                    gridladder.firstChild.lastChild.setAttribute("rowId",str(rowId))
                    rowId += 1
                    
                    gridladder.firstChild.lastChild.appendChild(gridladder.createElement("Column"))
                    gridladder.firstChild.lastChild.lastChild.setAttribute("columnId","0")
                    gridladder.firstChild.lastChild.lastChild.setAttribute("networkId",str(networkNumber))
            elif elly.getElementsByTagName("ParallelBranch"):
                print("New ParallelBranch Short Circuit")
        #inVariable (these are your inputs to blocks)
        if elly.nodeName == "inVariable":
            print("NewInvariable")
        #block (FB, F etc)
        if elly.nodeName == "block":
            print("NewBlock")
            mydistance = howCloseAmIToLeftPowerRail(0,ladder,elly)
            if mydistance > widthOfLadder:
                widthOfLadder = mydistance
  
            if elly.getElementsByTagName("connection")[0].getAttribute("refLocalId") == "0":
                # that means it's leftmost object
                if gridladder.firstChild.lastChild.lastChild.getAttribute("columnId") != "0":
                    # that means we parallel branch and need a new row
                    gridladder.firstChild.appendChild(gridladder.createElement("row"))
                    gridladder.firstChild.lastChild.setAttribute("rowId",str(rowId))
                    rowId += 1
                gridladder.firstChild.lastChild.appendChild(gridladder.createElement("Column"))
                gridladder.firstChild.lastChild.lastChild.setAttribute("columnId","1")
                gridladder.firstChild.lastChild.lastChild.setAttribute("localId",elly.getAttribute("localId"))
                gridladder.firstChild.lastChild.lastChild.setAttribute("width","3")
            else: # that means it points behind another object
                highestRowId = "65535"
                widestColumn = "0"
                for ellysUpstream in elly.getElementsByTagName("connection"):
                    # may be more than one upstream. 
                    # The first downstream will be in the highest row of the upstream and one column back from the furthest back upstream
                    ellysUpstreamId = ellysUpstream.getAttribute("refLocalId")
                    for gridellys in gridladder.firstChild.getElementsByTagName("Column"):
                        if gridellys.getAttribute("localId") == ellysUpstreamId:
                            if highestRowId > gridellys.parentNode.getAttribute("rowId"):
                                highestRowId = gridellys.parentNode.getAttribute("rowId")
                                highestRow = gridellys.parentNode
                            widestColumn = max(widestColumn, str(int(gridellys.getAttribute("columnId")) + int(gridellys.getAttribute("width"))))
                # Is there anyone at that RowColumn?
                someoneIsThereAlready = False
                for existingColumns in highestRow.getElementsByTagName("Column"):
                    if existingColumns.getAttribute("columnId") >= widestColumn:
                        someoneIsThereAlready = True
                if not someoneIsThereAlready :
                    highestRow.appendChild(gridladder.createElement("Column"))
                    highestRow.lastChild.setAttribute("columnId",widestColumn)
                    highestRow.lastChild.setAttribute("localId",elly.getAttribute("localId"))
                    highestRow.lastChild.setAttribute("width","3")
                # TODO Do something else if someone is already there
                # TODO You will have to flag that it is parallel
        #contact
        if elly.nodeName == "contact":
            if elly.getAttribute("negated") == "true":
                print("NewNegatedContact")
            else:
                print("NewContact")
  
            mydistance = howCloseAmIToLeftPowerRail(0,ladder,elly)
            if mydistance > widthOfLadder:
                widthOfLadder = mydistance
  
            if elly.getElementsByTagName("connection")[0].getAttribute("refLocalId") == "0":
                # that means it's leftmost object
                if gridladder.firstChild.lastChild.lastChild.getAttribute("columnId") != "0":
                    # that means we parallel branch and need a new row
                    gridladder.firstChild.appendChild(gridladder.createElement("row"))
                    gridladder.firstChild.lastChild.setAttribute("rowId",str(rowId))
                    rowId += 1
                gridladder.firstChild.lastChild.appendChild(gridladder.createElement("Column"))
                gridladder.firstChild.lastChild.lastChild.setAttribute("columnId","1")
                gridladder.firstChild.lastChild.lastChild.setAttribute("localId",elly.getAttribute("localId"))
                gridladder.firstChild.lastChild.lastChild.setAttribute("width","1")
            else: # that means it points behind another object
                highestRowId = "65535"
                widestColumn = "0"
                for ellysUpstream in elly.getElementsByTagName("connection"):
                    # may be more than one upstream. 
                    # The first downstream will be in the highest row of the upstream and one column back from the furthest back upstream
                    ellysUpstreamId = ellysUpstream.getAttribute("refLocalId")
                    for gridellys in gridladder.firstChild.getElementsByTagName("Column"):
                        if gridellys.getAttribute("localId") == ellysUpstreamId:
                            if highestRowId > gridellys.parentNode.getAttribute("rowId"):
                                highestRowId = gridellys.parentNode.getAttribute("rowId")
                                highestRow = gridellys.parentNode
                            widestColumn = max(widestColumn, str(int(gridellys.getAttribute("columnId")) + int(gridellys.getAttribute("width"))))
                # Is there anyone at that RowColumn?
                someoneIsThereAlready = False
                for existingColumns in highestRow.getElementsByTagName("Column"):
                    if existingColumns.getAttribute("columnId") >= widestColumn:
                        someoneIsThereAlready = True
                if not someoneIsThereAlready :
                    highestRow.appendChild(gridladder.createElement("Column"))
                    highestRow.lastChild.setAttribute("columnId",widestColumn)
                    highestRow.lastChild.setAttribute("localId",elly.getAttribute("localId"))
                    highestRow.lastChild.setAttribute("width","1")
                # TODO Do something else if someone is already there
                # TODO You will have to flag that it is parallel


  
        #coil
        if elly.nodeName == "coil":
            if elly.getAttribute("storage") == "none":
                print("NewCoil")
            elif elly.getAttribute("storage") == "set":
                print("NewSetCoil")
            elif elly.getAttribute("storage") == "reset":
                print("NewResetCoil")
            
            mydistance = howCloseAmIToLeftPowerRail(0,ladder,elly)
            if mydistance > widthOfLadder:
                widthOfLadder = mydistance
  
            if elly.getElementsByTagName("connection")[0].getAttribute("refLocalId") == "0":
                # that means it's leftmost object
                if gridladder.firstChild.lastChild.lastChild.getAttribute("columnId") != "0":
                    # that means we parallel branch and need a new row
                    gridladder.firstChild.appendChild(gridladder.createElement("row"))
                    gridladder.firstChild.lastChild.setAttribute("rowId",str(rowId))
                    rowId += 1
                gridladder.firstChild.lastChild.appendChild(gridladder.createElement("Column"))
                gridladder.firstChild.lastChild.lastChild.setAttribute("columnId","1")
                gridladder.firstChild.lastChild.lastChild.setAttribute("localId",elly.getAttribute("localId"))
                gridladder.firstChild.lastChild.lastChild.setAttribute("width","1")
            else: # that means it points behind another object
                highestRowId = "65535"
                widestColumn = "0"
                for ellysUpstream in elly.getElementsByTagName("connection"):
                    # may be more than one upstream. 
                    # The first downstream will be in the highest row of the upstream and one column back from the furthest back upstream
                    ellysUpstreamId = ellysUpstream.getAttribute("refLocalId")
                    for gridellys in gridladder.firstChild.getElementsByTagName("Column"):
                        if gridellys.getAttribute("localId") == ellysUpstreamId:
                            if highestRowId > gridellys.parentNode.getAttribute("rowId"):
                                highestRowId = gridellys.parentNode.getAttribute("rowId")
                                highestRow = gridellys.parentNode
                            widestColumn = max(widestColumn, str(int(gridellys.getAttribute("columnId")) + int(gridellys.getAttribute("width"))))
                # Is there anyone at that RowColumn?
                someoneIsThereAlready = False
                for existingColumns in highestRow.getElementsByTagName("Column"):
                    if existingColumns.getAttribute("columnId") >= widestColumn:
                        someoneIsThereAlready = True
                if not someoneIsThereAlready :
                    highestRow.appendChild(gridladder.createElement("Column"))
                    highestRow.lastChild.setAttribute("columnId",widestColumn)
                    highestRow.lastChild.setAttribute("localId",elly.getAttribute("localId"))
                    highestRow.lastChild.setAttribute("width","1")
                # TODO Do something else if someone is already there
                # TODO You will have to flag that it is parallel

        #rightPowerRail (think CODESYS were having a laugh)
    # How wide to make network? well:
    # 1. contacts and coils = 1
    # 2. blocks and executes = 3
    # 3. branches themselves don't take up additional room
    # 4. but... you have to work out the longest path on each branch
    # Data is presented localId + refLocalId [+ refLocalId + ...]
    # So I guess it is easiest to work our way backwards.  (from right to left)
    # We will naïvely look at every localId and how long it takes to get back to refLocalId=0
    # We also naïvely look at every possible localId, even if we've already found him
    # We've actually already calculated that above, yay for me!
    print(widthOfLadder)
  
    # Array of networks
    # Network is an array of Contact / Block / Coil
    # So we know it is (widthOfLadder) wide... so we could have an array.. no that's not right
    # 1. Pass through every element
    # 2. hmm, lets make a new ladder xml, which is the rows... input variables and labels can be added last
    # 3. <Row>
    #       <Column columnId="0" refLocalId=(localID of vendorElement new network) />
    #       <Column columnId="1" refLocalId=(localID of next contact/block/coil) />
    # 4. The next element comes in. It asks if anybody already has the row right behind its refLocalId
    #     If someone is already there, it starts a new Row, starting at the columnId just behind
    # 5. If it has two refLocalIds, it tries to get the furthest back column
    print (str(gridladder.toprettyxml()))
  
    # should now have a declaration section
    # a bunch of inputs
    # a bunch of outputs
    # and a bunch of blocks
    # convert them all to a nice easy format
    print ("{Declaration}")
    print (declarationheader)
    print ("{/Declaration}")
else:
    print("Maybe I want one argument, whch is the PLCOPEN XML.")
    print("Will output to current directory as Ladder0.ladder")