Python: xml uitbreiden aan de hand van excel

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • thomke
  • Registratie: November 2011
  • Laatst online: 15:30
hey!

Ik zou graag een python scriptje maken die het volgende doet:

het inlezen van een Excel bestand (deze bevat 'motor namen')
vervolgens aan de hand van het aantal element (namen van motoren) die in deze excel zitten een .xml bestand aanpassen.

er is een 'basic.xml' file dat als het ware als template bedoeld is.

code:
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
<?xml version="1.0" encoding="utf-8"?>
<Document>
  <Engineering version="V18" />
  <SW.Blocks.FB ID="0">
    <AttributeList>
      <Interface><Sections xmlns="http://www.siemens.com/automation/Openness/SW/Interface/v5">
  <Section Name="Input" />
  <Section Name="Output" />
  <Section Name="InOut" />
  <Section Name="Static" />
  <Section Name="Temp" />
  <Section Name="Constant" />
</Sections></Interface>
      <MemoryLayout>Optimized</MemoryLayout>
      <MemoryReserve>100</MemoryReserve>
      <Name>FB_PT_Motors</Name>
      <Namespace />
      <Number>2</Number>
      <ProgrammingLanguage>LAD</ProgrammingLanguage>
      <SetENOAutomatically>false</SetENOAutomatically>
    </AttributeList>
    <ObjectList>
      <MultilingualText ID="1" CompositionName="Comment">
        <ObjectList>
          <MultilingualTextItem ID="2" CompositionName="Items">
            <AttributeList>
              <Culture>en-US</Culture>
              <Text />
            </AttributeList>
          </MultilingualTextItem>
        </ObjectList>
      </MultilingualText>
      <SW.Blocks.CompileUnit ID="3" CompositionName="CompileUnits">
        <AttributeList>
          <NetworkSource><FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4">
  <Parts>
    <Access Scope="LiteralConstant" UId="21">
      <Constant>
        <ConstantType>String</ConstantType>
        <ConstantValue>'partmotor'</ConstantValue>
      </Constant>
    </Access>
    <Access Scope="GlobalVariable" UId="22">
      <Symbol>
        <Component Name="DB_PT" />
        <Component Name="Motors" />
        <Component Name="partmotor" />
      </Symbol>
    </Access>
    <Access Scope="GlobalVariable" UId="23">
      <Symbol>
        <Component Name="Processes" />
        <Component Name="PT-MF" />
      </Symbol>
    </Access>
    <Call UId="24">
      <CallInfo Name="FB_Motor" BlockType="FB">
        <Instance Scope="GlobalVariable" UId="25">
          <Component Name="IDBM_partmotor" />
        </Instance>
        <Parameter Name="I_sName" Section="Input" Type="String" />
        <Parameter Name="I_sELNum" Section="Input" Type="String" />
        <Parameter Name="I_Type" Section="Input" Type="Byte" />
        <Parameter Name="I_bSafetyOK" Section="Input" Type="Bool" />
        <Parameter Name="I_bMSOK" Section="Input" Type="Bool" />
        <Parameter Name="I_bEDM_Error" Section="Input" Type="Bool" />
        <Parameter Name="I_bEnableFW" Section="Input" Type="Bool" />
        <Parameter Name="I_bEnableBw" Section="Input" Type="Bool" />
        <Parameter Name="I_bTP" Section="Input" Type="Bool" />
        <Parameter Name="I_bRD" Section="Input" Type="Bool" />
        <Parameter Name="I_lrRDtime" Section="Input" Type="LReal" />
        <Parameter Name="I_bInterlockManual" Section="Input" Type="Bool" />
        <Parameter Name="I_bInterlockAuto" Section="Input" Type="Bool" />
        <Parameter Name="I_bAuto_Stop" Section="Input" Type="Bool" />
        <Parameter Name="I_bAuto_FW" Section="Input" Type="Bool" />
        <Parameter Name="I_bAuto_BW" Section="Input" Type="Bool" />
        <Parameter Name="I_lrMaxSpeed" Section="Input" Type="LReal" />
        <Parameter Name="I_lrMinSpeed" Section="Input" Type="LReal" />
        <Parameter Name="I_lrStartUpTime" Section="Input" Type="LReal" />
        <Parameter Name="I_lrShutdownTime" Section="Input" Type="LReal" />
        <Parameter Name="I_lrTimeToRunEmpty" Section="Input" Type="LReal" />
        <Parameter Name="I_lrAutoSpeed" Section="Input" Type="LReal" />
        <Parameter Name="HW_ID_Drive" Section="Input" Type="HW_SUBMODULE" />
        <Parameter Name="Q_ID" Section="Output" Type="DInt" />
        <Parameter Name="Q_lrSpeedToDrive" Section="Output" Type="LReal" />
        <Parameter Name="Q_bRunFW" Section="Output" Type="Bool" />
        <Parameter Name="Q_bRunBW" Section="Output" Type="Bool" />
        <Parameter Name="UDT_Motor" Section="InOut" Type="&quot;UDT_Motor_HMI&quot;" />
        <Parameter Name="UDT_Process" Section="InOut" Type="&quot;UDT_Process&quot;" />
      </CallInfo>
    </Call>
  </Parts>
  <Wires>
    <Wire UId="52">
      <Powerrail />
      <NameCon UId="24" Name="en" />
    </Wire>
    <Wire UId="53">
      <IdentCon UId="21" />
      <NameCon UId="24" Name="I_sName" />
    </Wire>
    <Wire UId="54">
      <OpenCon UId="26" />
      <NameCon UId="24" Name="I_sELNum" />
    </Wire>
    <Wire UId="55">
      <OpenCon UId="27" />
      <NameCon UId="24" Name="I_Type" />
    </Wire>
    <Wire UId="56">
      <OpenCon UId="28" />
      <NameCon UId="24" Name="I_bSafetyOK" />
    </Wire>
    <Wire UId="57">
      <OpenCon UId="29" />
      <NameCon UId="24" Name="I_bMSOK" />
    </Wire>
    <Wire UId="58">
      <OpenCon UId="30" />
      <NameCon UId="24" Name="I_bEDM_Error" />
    </Wire>
    <Wire UId="59">
      <OpenCon UId="31" />
      <NameCon UId="24" Name="I_bEnableFW" />
    </Wire>
    <Wire UId="60">
      <OpenCon UId="32" />
      <NameCon UId="24" Name="I_bEnableBw" />
    </Wire>
    <Wire UId="61">
      <OpenCon UId="33" />
      <NameCon UId="24" Name="I_bTP" />
    </Wire>
    <Wire UId="62">
      <OpenCon UId="34" />
      <NameCon UId="24" Name="I_bRD" />
    </Wire>
    <Wire UId="63">
      <OpenCon UId="35" />
      <NameCon UId="24" Name="I_lrRDtime" />
    </Wire>
    <Wire UId="64">
      <OpenCon UId="36" />
      <NameCon UId="24" Name="I_bInterlockManual" />
    </Wire>
    <Wire UId="65">
      <OpenCon UId="37" />
      <NameCon UId="24" Name="I_bInterlockAuto" />
    </Wire>
    <Wire UId="66">
      <OpenCon UId="38" />
      <NameCon UId="24" Name="I_bAuto_Stop" />
    </Wire>
    <Wire UId="67">
      <OpenCon UId="39" />
      <NameCon UId="24" Name="I_bAuto_FW" />
    </Wire>
    <Wire UId="68">
      <OpenCon UId="40" />
      <NameCon UId="24" Name="I_bAuto_BW" />
    </Wire>
    <Wire UId="69">
      <OpenCon UId="41" />
      <NameCon UId="24" Name="I_lrMaxSpeed" />
    </Wire>
    <Wire UId="70">
      <OpenCon UId="42" />
      <NameCon UId="24" Name="I_lrMinSpeed" />
    </Wire>
    <Wire UId="71">
      <OpenCon UId="43" />
      <NameCon UId="24" Name="I_lrStartUpTime" />
    </Wire>
    <Wire UId="72">
      <OpenCon UId="44" />
      <NameCon UId="24" Name="I_lrShutdownTime" />
    </Wire>
    <Wire UId="73">
      <OpenCon UId="45" />
      <NameCon UId="24" Name="I_lrTimeToRunEmpty" />
    </Wire>
    <Wire UId="74">
      <OpenCon UId="46" />
      <NameCon UId="24" Name="I_lrAutoSpeed" />
    </Wire>
    <Wire UId="75">
      <OpenCon UId="47" />
      <NameCon UId="24" Name="HW_ID_Drive" />
    </Wire>
    <Wire UId="76">
      <IdentCon UId="22" />
      <NameCon UId="24" Name="UDT_Motor" />
    </Wire>
    <Wire UId="77">
      <IdentCon UId="23" />
      <NameCon UId="24" Name="UDT_Process" />
    </Wire>
    <Wire UId="78">
      <NameCon UId="24" Name="Q_ID" />
      <OpenCon UId="48" />
    </Wire>
    <Wire UId="79">
      <NameCon UId="24" Name="Q_lrSpeedToDrive" />
      <OpenCon UId="49" />
    </Wire>
    <Wire UId="80">
      <NameCon UId="24" Name="Q_bRunFW" />
      <OpenCon UId="50" />
    </Wire>
    <Wire UId="81">
      <NameCon UId="24" Name="Q_bRunBW" />
      <OpenCon UId="51" />
    </Wire>
  </Wires>
</FlgNet></NetworkSource>
          <ProgrammingLanguage>LAD</ProgrammingLanguage>
        </AttributeList>
        <ObjectList>
          <MultilingualText ID="4" CompositionName="Comment">
            <ObjectList>
              <MultilingualTextItem ID="5" CompositionName="Items">
                <AttributeList>
                  <Culture>en-US</Culture>
                  <Text />
                </AttributeList>
              </MultilingualTextItem>
            </ObjectList>
          </MultilingualText>
          <MultilingualText ID="6" CompositionName="Title">
            <ObjectList>
              <MultilingualTextItem ID="7" CompositionName="Items">
                <AttributeList>
                  <Culture>en-US</Culture>
                  <Text>partmotor</Text>
                </AttributeList>
              </MultilingualTextItem>
            </ObjectList>
          </MultilingualText>
        </ObjectList>
      </SW.Blocks.CompileUnit>
      <MultilingualText ID="D" CompositionName="Title">
        <ObjectList>
          <MultilingualTextItem ID="E" CompositionName="Items">
            <AttributeList>
              <Culture>en-US</Culture>
              <Text />
            </AttributeList>
          </MultilingualTextItem>
        </ObjectList>
      </MultilingualText>
    </ObjectList>
  </SW.Blocks.FB>
</Document>



zoals je kan zien (makkelijker als je de file opent met chrome of iets dergelijks),
zit er in deze basic.xml een element:

code:
1
<SW.Blocks.CompileUnit ID="3" CompositionName="CompileUnits">


(regel 33 tot en met 240 )

nu wil ik een python script maken die voor iedere motor in de excel zo een compleet 'CompileUnit'(regel 33 tot en met 240) element bij maakt. maar natuurlijk moeten er een paar dingen aangepast te worden:
overal waar 'partmotor' staat dient de tekst aangepast te worden naar de naam van de motor in de excel file

alsook moet de 'ID' verder optellen. maar ook de 'ID's binnen deze CompileUnit element moeten optellen:
zo kan je zien dat naar het eide van dit element ook nog ID's 4, 5, 6 en 7 te vinden zijn.

dit wil zeggen dat de 2e CompileUnit ID="8" moet zijn.
code:
1
<SW.Blocks.CompileUnit ID="8" CompositionName="CompileUnits">

de ID's binnen deze CompileUnit zijn dan 9, 10 , 11 en 12.

om het wat duidelijker te maken heb ik hier 3 bestanden gepost:
https://github.com/AdremTDE/xml_based_on_xlsx

in deze excel zitten nu 2 motoren: Motor_1 en Motor_2 en heb ik een voorbeeld gemaakt (manueel) hoe de nieuwe xml er zou moeten uit zien dat het python scriptje maakt.
output_example.xml

De reden dat ik dit automatisch wil laten creeren is omdat ik dit dan kan importeren in een software pakket wat mij heel veel tijd zou besparen (de officele excel zal zo 100 motoren bevatten, en kan ik dan ook variaties op maken voor andere componenten)


In theorie zou je een repetetieve taak moeten kunnen automatiseren, maar ik heb hier nu reeds verschillende python scriptjes op los gelaten maar nog nooit het gewenste resultaat gehad.

(ik heb dit reeds geprobeerd met behulp van ChatGPT (Dit heeft mij al veel geholpen met het maken van python scriptjes die wel werkbaar waren of na enkele aanpassingen ik het werkend gekregen heb.. maar hierbij ben ik nog niet op een werkend script uitgekomen..

Als er mensen zijn die mij hiermee kunnen helpen zou dit mij heel gelukkig maken!

waar het volgens mij onder andere mis loopt met de code die Chat gpt mij voorziet:
code:
1
2
3
compile_unit_template = root.find(".//SW.Blocks.CompileUnit[@ID='3']")
    new_compile_unit = ET.Element('SW.Blocks.CompileUnit', {'ID': str(int(compile_unit_template.attrib['ID']) + 1),
                                                             'CompositionName': compile_unit_template.attrib['CompositionName']})


is dat hij niet alles mee neemt van binnen in dit element om op verder te werken.

[ Voor 204% gewijzigd door thomke op 06-09-2023 17:23 ]

Alle reacties


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Die vragen die er staan wanneer je hier een nieuw topic opent staan er natuurlijk niet voor niets:
Mijn vraag
...

Relevante software en hardware die ik gebruik
...

Wat ik al gevonden of geprobeerd heb
...
Zoals je in onze Quickstart kunt lezen zien we graag wat je zelf al gezocht/gevonden/geprobeerd hebt. En dan niet "ik heb al wat scriptjes van internet geprobeerd" want dat zegt ons natuurlijk helemaal niets zonder die scriptjes te hebben gezien. ChatGPT is nog een brug verder; dat moet je gebruiken als je code die 't ding genereert kunt aanpassen naar je wens. Als je zelf niet kunt programmeren dan vrees ik dat we je niet echt kunnen helpen; we gaan je in ieder geval niet aan het handje houden een stap-voor-stap een script met je schrijven. Niet omdat we gemeen zijn, maar omdat dat hier gewoon niet de bedoeling is.

We willen je best tips geven over hoe zoiets aan te pakken of naar je huidige code kijken waar we eventueel een probleem zien. Maar een aantal bestanden 'dumpen' met de vraag "dit-en-dat heb ik en dat moet zus-en-zo worden" is dus niet de bedoeling.

Verder: Wanneer je code (of XML) post, probeer 't dan even een beetje te beperken tot relevante(!) stukjes / snippets a-la zoiets:

XML:
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
<?xml version="1.0" encoding="utf-8"?>
<Document>
    <Engineering version="V18" />
    <SW.Blocks.FB ID="0">
            ...
        <ObjectList>
            ...
            <SW.Blocks.CompileUnit ID="3" CompositionName="CompileUnits">
                <AttributeList>
                    <NetworkSource>
                        <FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4">
                            <Parts>
                                ...
                            </Parts>
                            <Wires>
                                <Wire UId="52">
                                    <Powerrail />
                                    <NameCon UId="24" Name="en" />
                                </Wire>
                                <Wire UId="53">
                                    <IdentCon UId="21" />
                                    <NameCon UId="24" Name="I_sName" />
                                </Wire>
                                ...
                                <Wire UId="80">
                                    <NameCon UId="24" Name="Q_bRunFW" />
                                    <OpenCon UId="50" />
                                </Wire>
                                <Wire UId="81">
                                    <NameCon UId="24" Name="Q_bRunBW" />
                                    <OpenCon UId="51" />
                                </Wire>
                            </Wires>
                        </FlgNet>
                    </NetworkSource>
                    ...
                </AttributeList>
                <ObjectList>
                    ...
                </ObjectList>
            </SW.Blocks.CompileUnit>
            ...
        </ObjectList>
    </SW.Blocks.FB>
</Document>


En na dat alles dus de vraag: Laat eens zien wat je nu hebt?

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • thomke
  • Registratie: November 2011
  • Laatst online: 15:30
@RobIII

Bedankt voor uw reactie.
het is inderdaad beter van die xml in te korten, ik had er niet aan gedacht om dit zo te doen
(om te voorkomen dat ik te veel ging posten had ik daarom de bestanden op github gezet)
maar deze manier is inderdaad wel overzichtelijker.

Python is redelijk nieuw voor mij (ik programeer meestal PLC's, dus weet ik ook wel dat dit even een ander niveau is.)

nu heb ik ondertussen al redelijk wat python scriptjes werkend gekregen en kunnen aanpassen zoals ik wil
(dankzij youtube tutorials en ik zal niet ontkennen dat Chat GPT een enorme hulp ook al geweest is.)
meestal gebruik ik chatgpt om mij op weg te helpen / een basis te creeren. en kan ik daar dan verder op inspelen en het werkend krijgen zoals ik wens.

Dit is het script zoals ik het nu heb (niet alle functionaliteit zit er al in die ik wens, maar eerst een basis en dan verder bouwen ;) ) ..

code:
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
import openpyxl
from lxml import etree
from copy import deepcopy

# Load the Excel file
wb = openpyxl.load_workbook(r'Motors.xlsx')
ws = wb.active

# Get the list of motor names from the Excel file
motor_names = [cell.value for cell in ws['A']]
print(motor_names)
# Parse the XML file
tree = etree.parse(r'basic.xml')
root = tree.getroot()

# Find the section to copy
compile_unit_template = root.find(".//SW.Blocks.CompileUnit")

# Loop through the motor names and create a new CompileUnit for each motor
for motor_name in motor_names:
    # Duplicate the CompileUnit element
    new_compile_unit = deepcopy(compile_unit_template)

    # Update the ID attributes
    new_id = str(int(new_compile_unit.get('ID')) + 1)
    new_compile_unit.set('ID', new_id)

    # Replace 'partmotor' with the 'motor name'
    for elem in new_compile_unit.iter():
        if elem.text == "'partmotor'":
            elem.text = f"'{motor_name}'"

    # Insert the new CompileUnit element
    compile_unit_template.addnext(new_compile_unit)

# Save the modified XML to a new file

tree.write('output.xml', encoding='utf-8')


functionaliteit die nog ontbreekt is onder andere het goed toepassen van die 'ID's ' (maar eerst wil ik dat de structuur juist staat om dan verder te gaan bouwen tot het gewenste resultaat)

De output van dit script past onder ander niet alle teksten 'partmotor' aan binnen de 'CompileUnit'

Acties:
  • +1 Henk 'm!

  • Tk55
  • Registratie: April 2009
  • Niet online
Om de nieuwe ID te bepalen, kun je gewoon helemaal aan het begin de hoogste ID bepalen. In pseudo code:
code:
1
2
3
4
max_id = 0
for compile_init in compile_units:
    if compile_unit[‘id’] > max_id:
        max_id = compile_unit[‘id’]


Zodra je dan een nieuwe compile unit toevoegt, moet je even de max_id updaten.

Verder eens met @RobIII, je mag je probleem wat duidelijker aangeven. Heb je ook al geprobeerd een debugger te gebruiken, of wat print statements toe te voegen?

Acties:
  • 0 Henk 'm!

  • Ben(V)
  • Registratie: December 2013
  • Laatst online: 13:20
'

[ Voor 100% gewijzigd door Ben(V) op 08-09-2023 09:13 ]

All truth passes through three stages: First it is ridiculed, second it is violently opposed and third it is accepted as being self-evident.


Acties:
  • +1 Henk 'm!

  • thomke
  • Registratie: November 2011
  • Laatst online: 15:30
voor de geïnteresseerden:

code:
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
import openpyxl
from lxml import etree
from copy import deepcopy


# Example: <Component Name="Motor 1"/>
def change_parameter_name(element, old, new):
    if element.get("Name") == old:
        element.set("Name", new)


# Example: <Value> Motor 1 </Value>
def change_value(element, old, new):
    if element.text == old:
        element.text = new


# Load the Excel file
wb = openpyxl.load_workbook(r'Motors.xlsx')
ws = wb.active

# Parse the XML file
tree = etree.parse(r'basic.xml')
root = tree.getroot()

# Find the section to copy from XML file
compile_unit_template = root.find(".//SW.Blocks.CompileUnit")

# Get the list of text values in column A
motor_names = [cell.value for cell in ws['A']]
valve_names = [cell.value for cell in ws['B']]
print(motor_names)

# Loop through the motor names and create a new CompileUnit for each motor
for motor_name in motor_names:

    # Duplicate the CompileUnit element
    new_compile_unit = deepcopy(compile_unit_template)

    # Replace partmotor with the 'motor name'
    for elem in new_compile_unit.iter():
        change_value(elem, "'partmotor'", f"'{motor_name}'")
        change_parameter_name(elem, "partmotor", motor_name)
        change_parameter_name(elem, "IDBM_partmotor", f'IDBM_{motor_name}')
        change_value(elem, "partmotor", motor_name)

    # Insert the new CompileUnit element
    compile_unit_template.addprevious(new_compile_unit)

# remove the original copy
compile_unit_template.getparent().remove(compile_unit_template)

# Replace all id's
max_id = 0
for elem in root.iter():
    if elem.get("ID") is not None and elem.get("ID").strip('"').isdigit():
        elem.set("ID", str(max_id))
        max_id = max_id + 1

# Save the modified XML to a new file
tree.write('output.xml', encoding='utf-8')

# Close the Excel file
wb.close()


(indien mensen optimalisaties/opmerkingen hebben over deze code... laat maar gerust weten ;) )
Pagina: 1