Skip to content

Commit b44ea87

Browse files
authored
Add files via upload
1 parent 0ac2283 commit b44ea87

File tree

5 files changed

+564
-0
lines changed

5 files changed

+564
-0
lines changed

AMLtool.py

Lines changed: 381 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
'''
2+
Santiago Montagud
3+
ESI Group
4+
'''
5+
6+
import tkinter as tk
7+
from tkinter import messagebox
8+
from tkinter import filedialog
9+
from pyautomationml import PyAutomationML
10+
from pyautomationml import AmlElement
11+
from pyautomationml import Element
12+
from lxml import etree
13+
import csv
14+
import os
15+
import json
16+
import datetime
17+
18+
class AML_GUI_App:
19+
def __init__(self, root):
20+
self.root = root
21+
self.root.title("Checkbox Selection")
22+
# Graphical configuration
23+
#self.root.geometry("600x400")
24+
25+
# parse configuration file
26+
self.mainfolder = os.getcwd()
27+
Configfolder = os.path.join(self.mainfolder, "src")
28+
Config_txt_file_name = 'Config.json'
29+
config_file_path = os.path.join(Configfolder, Config_txt_file_name)
30+
self.parse_config_file(config_file_path)
31+
32+
self.AML_master_path = os.path.join(self.mainfolder, self.automationml_files_folder, self.master_file_name)
33+
self.AutomationMLtemplate_path = os.path.join(self.mainfolder, self.automationml_files_folder, self.template_file_name)
34+
self.W_IDs_txt_path = os.path.join(self.mainfolder, self.sources_file, self.W_IDs_txt_file_name)
35+
self.PathOfDoE = 'Not DoE selected' # Default value
36+
self.workflow_column_name = "Workflow Options" # Name to be shown on the App
37+
self.outputs_name = "Output Options" # Name to be shown on the App
38+
self.selections = {self.workflow_column_name: None, self.outputs_name: []}
39+
40+
#Names of Internal Elements
41+
self.Master_WorkflowsIE_name = 'Workflows' # Interal element where the IDs of the workflows are stored to avoid repetition of IDs
42+
self.Workflow_InformationIE_name = 'Information'
43+
self.Workflow_DefinitionIE_name = 'Workflow_definition'
44+
self.Information_Sequence_type_name = 'Sequence_type'
45+
self.InputsIE_name = 'Inputs'
46+
self.DoEIE_name = 'DoE'
47+
self.OutputsIE_name = 'Outputs'
48+
self.PhasesIE_name = 'Phases'
49+
self.Software_softwareID_name = 'software_ID'
50+
self.Software_Parameters_values_name = 'values'
51+
self.Software_Parameters_OutputFile_name = 'output_file'
52+
53+
# Create the titles, bottons, etc.
54+
self.create_widgets()
55+
56+
# Read configuration file and store information in internal variables
57+
# strings are to be modified in the script and in the config file with the same string characters
58+
def parse_config_file(self, config_file_path):
59+
with open(config_file_path, "r") as json_file:
60+
data = json.load(json_file)
61+
self.master_file_name = data['master_file_name']
62+
self.template_file_name = data['template_file_name']
63+
self.Workflow_options = data['Workflow_options']
64+
self.output_options = data['output_options']
65+
self.W_IDs_txt_file_name = data['W_IDs_txt_file_name']
66+
self.automationml_files_folder = data['automationml_files_folder']
67+
self.sources_file = data['sources_file']
68+
self.Predefined_workflows = data['Predefined_workflows']
69+
self.Models = data['Models']
70+
71+
def create_AML_element(self, parent, Name, text=None):
72+
# default:
73+
context_global = {}
74+
context_local = {}
75+
# Define the tag, attributes, and text for the new element
76+
tag = "{http://www.dke.de/CAEX}InternalElement"
77+
attribute = {"Name": Name}
78+
new_aml_element = Element(tag, attrib=attribute, text=text, context_global=context_global, context_local=context_local)
79+
parent.append(new_aml_element.__element__)
80+
81+
def add_attribute_on_AML_element(self, Internal_Element_obj, attribute_name):
82+
new_attribute = etree.Element("Attribute", Name=attribute_name, AttributeDataType="xs:string")
83+
Internal_Element_obj.append(new_attribute)
84+
85+
86+
def get_value_AML_element(self, AML, InternalElement, attribute_name):
87+
IE = AML.root.find(".//*[@Name={}]".format(InternalElement))
88+
Atrbt = IE.find(".//*[@Name={}]".format(attribute_name))
89+
value = Atrbt.__element__.Value
90+
return value
91+
92+
def get_max_value_AML_workflow(self,AML, InternalElement):
93+
IE = AML.find(f"./*[@Name='{InternalElement}']")
94+
max_value = 0
95+
for atribute in IE.iterchildren():
96+
value = int(atribute.__element__.attrib['Name'].replace("w", ""))
97+
if value > max_value:
98+
max_value = value
99+
return max_value
100+
101+
def find_all_children_by_name(self, element, name, matching_elements=None):
102+
if matching_elements is None:
103+
matching_elements = []
104+
105+
if element.get('Name') == name:
106+
matching_elements.append(element)
107+
108+
for child in element.iterchildren():
109+
self.find_all_children_by_name(child, name, matching_elements)
110+
111+
return matching_elements
112+
113+
def write_value_on_AML_attribute(self, InternalElementObj, attribute_name, value):
114+
Atrbt = InternalElementObj.find(f"./*[@Name='{attribute_name}']")
115+
Atrbt.__element__.Value = value
116+
117+
def write_ID_w_on_txt(self, W_IDs_txt_path, value):
118+
with open(W_IDs_txt_path, 'a') as f:
119+
f.write(str(value) + '\n')
120+
121+
def create_widgets(self):
122+
#Select DoE
123+
self.frame_Select_DoE = tk.Frame(self.root)
124+
self.frame_Select_DoE.grid(row=0, column=0, padx=10, pady=5, sticky="n")
125+
self.select_DoE_button = tk.Button(self.frame_Select_DoE, text="Select DoE", command=self.select_DoE)
126+
self.select_DoE_button.pack()
127+
128+
#Select Workflow
129+
self.workflow_frame = tk.Frame(self.root)
130+
self.workflow_frame.grid(row=0, column=1, padx=10, pady=5, sticky="n")
131+
self.workflow_label = tk.Label(self.workflow_frame, text=self.workflow_column_name)
132+
self.workflow_label.pack()
133+
self.show_radio_buttons(self.Workflow_options, self.workflow_frame, self.workflow_column_name)
134+
135+
#Select Outputs
136+
self.outputs_frame = tk.Frame(self.root)
137+
self.outputs_frame.grid(row=0, column=2, padx=10, pady=5, sticky="n")
138+
self.outputs_label = tk.Label(self.outputs_frame, text=self.outputs_name)
139+
self.outputs_label.pack()
140+
self.show_checkboxes(self.output_options, self.outputs_frame, self.outputs_name)
141+
142+
#Save
143+
self.save_AML_button = tk.Button(self.root, text="Save to AutomationML", command=self.save_AML)
144+
self.save_AML_button.grid(row=1, columnspan=2, padx=10, pady=10, sticky="e")
145+
146+
def select_DoE(self):
147+
file_path = filedialog.askopenfilename(filetypes=[("CSV Files", "*.csv")])
148+
if file_path:
149+
try:
150+
with open(file_path, 'r') as file:
151+
csv_reader = csv.reader(file)
152+
data = [next(csv_reader) for _ in range(3)]
153+
print(data) # Update WW with the data or do other processing
154+
155+
# Save the path of the DoE file
156+
self.PathOfDoE = file_path
157+
print("DoE Path:", self.PathOfDoE)
158+
159+
except Exception as e:
160+
print("Error reading CSV file:", e)
161+
162+
def show_radio_buttons(self, options, frame, set_name):
163+
var = tk.StringVar()
164+
for option in options:
165+
radio_button = tk.Radiobutton(
166+
frame, text=option, variable=var, value=option
167+
)
168+
radio_button.pack(anchor="w", padx=5, pady=2)
169+
self.selections[set_name] = var
170+
171+
def show_checkboxes(self, options, frame, set_name):
172+
for option in options:
173+
var = tk.StringVar(value="")
174+
checkbox = tk.Checkbutton(
175+
frame, text=option, variable=var, onvalue=option, offvalue=""
176+
)
177+
checkbox.pack(anchor="w", padx=5, pady=2)
178+
self.selections[set_name].append(var)
179+
180+
def write_on_AML(self):
181+
rootmaster = PyAutomationML(self.AML_master_path)
182+
183+
if os.path.exists(self.AutomationMLtemplate_path):
184+
roottemplate = PyAutomationML(self.AutomationMLtemplate_path)
185+
else:
186+
print(f"The path '{self.AutomationMLtemplate_path}' does not exist.")
187+
188+
# Get the root of the master file
189+
self.master = rootmaster.root.find("./*[@Name='Workflow']")
190+
191+
# Get the root of the AutomationML template
192+
self.workflow_instance = roottemplate.root.find("./*[@Name='Workflow']")
193+
194+
# get workflow last value
195+
value = self.get_max_value_AML_workflow(self.master, self.Master_WorkflowsIE_name)
196+
print(f'Old workflow ID is {value}')
197+
new_workflow_number = value + 1
198+
print(f'New workflow ID is {new_workflow_number}')
199+
200+
# write ID number on txt file (keep an extra copy of the information to avoid misuse)
201+
self.write_ID_w_on_txt(self.W_IDs_txt_path, new_workflow_number)
202+
203+
# Set AML ID name like 'w + ID number + .aml'
204+
self.workflow_instance_name = 'w' + str(new_workflow_number)
205+
print(f'New workflow name is {self.workflow_instance_name}')
206+
self.template_instance_name = 'w' + str(new_workflow_number) + '.aml'
207+
print(f'New workflow file name is {self.template_instance_name}')
208+
self.template_instance_path = os.path.join(self.automationml_files_folder, self.template_instance_name)
209+
210+
# write ID on master file as an attribute in Workflows IE and as an element
211+
WorkflowsObj = self.master.find(f"./*[@Name='{self.Master_WorkflowsIE_name}']")
212+
self.add_attribute_on_AML_element(WorkflowsObj, self.workflow_instance_name)
213+
self.create_AML_element(WorkflowsObj, self.workflow_instance_name)
214+
Workflow_instance_Obj = WorkflowsObj.find(f".//{{http://www.dke.de/CAEX}}InternalElement[@Name='{self.workflow_instance_name}']")
215+
self.add_attribute_on_AML_element(Workflow_instance_Obj, 'path')
216+
217+
# write selected workflow type in AutomationML template on information IE on the text of the existing attribute 'Sequence_type'
218+
InformationIE = self.workflow_instance.find(f"./*[@Name='{self.Workflow_InformationIE_name}']")
219+
self.write_value_on_AML_attribute(InformationIE, 'ID', self.workflow_instance_name)
220+
self.write_value_on_AML_attribute(InformationIE, self.Information_Sequence_type_name, self.selections[self.workflow_column_name].get())
221+
current_date = datetime.date.today()
222+
formatted_date = current_date.strftime("%Y/%m/%d")
223+
self.write_value_on_AML_attribute(InformationIE,'Date', str(formatted_date))
224+
225+
Info_OutputsIE = InformationIE.find("./*[@Name='Outputs']")
226+
selected_output_options = [var.get() for var in self.selections[self.outputs_name]]
227+
self.filtered_selected_output_options = [item for item in selected_output_options if item.strip() != ""]
228+
for output_element in self.filtered_selected_output_options:
229+
self.create_AML_element(Info_OutputsIE, output_element)
230+
231+
# write selected workflow information in AutomationML template on Workflow Definition for execution
232+
233+
# write phases when they exist
234+
Workflow_DefinitionIE = self.workflow_instance.find(f"./*[@Name='{self.Workflow_DefinitionIE_name}']")
235+
PhasesIE = Workflow_DefinitionIE.find(f"./*[@Name='{self.PhasesIE_name}']")
236+
# extract the models:
237+
Selected_Workflow_Simulations = self.Predefined_workflows[self.selections[self.workflow_column_name].get()]
238+
Preprocess_models = [element for element in self.Models["Preprocess"] if element in Selected_Workflow_Simulations]
239+
Simulation_models = [element for element in self.Models["Simulation"] if element in Selected_Workflow_Simulations]
240+
Postprocess_models = [element for element in self.Models["Postprocess"] if element in Selected_Workflow_Simulations]
241+
# 1a. Preprocess
242+
Bool_Preproc_models = any(element in self.Models["Preprocess"] for element in self.Predefined_workflows[self.selections[self.workflow_column_name].get()])
243+
if Bool_Preproc_models:
244+
self.create_AML_element(PhasesIE, 'Preprocess')
245+
PreprocessIE = PhasesIE.find("./*[@Name='Preprocess']")
246+
#create chain of charaters for HPC:
247+
chain_string = ""
248+
for element in Preprocess_models:
249+
chain_string += f'{element}.run>>'
250+
chain_string = chain_string.rstrip('>>')
251+
#add attribute 'Sequence':
252+
self.add_attribute_on_AML_element(PreprocessIE, 'Sequence')
253+
#add chain to attribute 'Sequence'
254+
self.write_value_on_AML_attribute(PreprocessIE, 'Sequence', chain_string)
255+
#create the phase internal elements
256+
for element in Preprocess_models:
257+
self.create_AML_element(PreprocessIE, element)
258+
#get the element just created
259+
ElementIE = PreprocessIE.find(f"./*[@Name='{element}']")
260+
#add ID attribute
261+
self.add_attribute_on_AML_element(ElementIE, self.Software_softwareID_name)
262+
#fill in ID attribute
263+
self.write_value_on_AML_attribute(ElementIE, self.Software_softwareID_name, element)
264+
#add Parameters IE
265+
self.create_AML_element(ElementIE, 'Parameters')
266+
#get Parameters IE
267+
ParametersIE = ElementIE.find("./*[@Name='Parameters']")
268+
#add output_file atribute to Parameters IE
269+
self.add_attribute_on_AML_element(ParametersIE, self.Software_Parameters_OutputFile_name)
270+
else:
271+
print("No preprocess")
272+
273+
# 1b. Preprocess
274+
Bool_Sim_models = any(element in self.Models["Simulation"] for element in self.Predefined_workflows[self.selections[self.workflow_column_name].get()])
275+
if Bool_Sim_models:
276+
self.create_AML_element(PhasesIE, 'Simulations')
277+
SimulationsIE = PhasesIE.find("./*[@Name='Simulations']")
278+
#create chain of charaters for HPC:
279+
chain_string = ""
280+
for element in Simulation_models:
281+
chain_string += f'{element}.run>>'
282+
chain_string = chain_string.rstrip('>>')
283+
#add it to attribute 'Sequence':
284+
self.add_attribute_on_AML_element(SimulationsIE, 'Sequence')
285+
#add chain to attribute 'Sequence'
286+
self.write_value_on_AML_attribute(SimulationsIE, 'Sequence', chain_string)
287+
#create the phase internal elements
288+
idx = 0
289+
for idx,element in enumerate(Simulation_models):
290+
self.create_AML_element(SimulationsIE, element)
291+
#get the element just created
292+
ElementIE = SimulationsIE.find(f"./*[@Name='{element}']")
293+
#add ID attribute
294+
self.add_attribute_on_AML_element(ElementIE, self.Software_softwareID_name)
295+
#fill in ID attribute
296+
self.write_value_on_AML_attribute(ElementIE, self.Software_softwareID_name, element)
297+
#add Parameters IE
298+
self.create_AML_element(ElementIE, 'Parameters')
299+
#get Parameters IE
300+
ParametersIE = ElementIE.find("./*[@Name='Parameters']")
301+
#add values atribute to Parameters IE
302+
self.add_attribute_on_AML_element(ParametersIE, self.Software_Parameters_values_name)
303+
self.write_value_on_AML_attribute(ParametersIE, 'values', '{doe_row}')
304+
#add output_file atribute to Parameters IE
305+
self.add_attribute_on_AML_element(ParametersIE, self.Software_Parameters_OutputFile_name)
306+
value_field = '{results_folder}/' + element + '_{line_number}.out'
307+
self.write_value_on_AML_attribute(ParametersIE, self.Software_Parameters_OutputFile_name, value_field)
308+
if idx > 0:
309+
#add inputs_file atribute to Parameters IE
310+
self.add_attribute_on_AML_element(ParametersIE, 'input_file')
311+
value_field = '{results_folder}/' + Simulation_models[idx-1] + '_{line_number}.out'
312+
self.write_value_on_AML_attribute(ParametersIE, 'input_file', value_field)
313+
314+
else:
315+
print("No simulations")
316+
317+
# 1c. Postprocess
318+
Bool_Postproc_models = any(element in self.Models["Postprocess"] for element in self.Predefined_workflows[self.selections[self.workflow_column_name].get()])
319+
if Bool_Postproc_models:
320+
self.create_AML_element(PhasesIE, 'Postprocess')
321+
PostprocessIE = PhasesIE.find("./*[@Name='Postprocess']")
322+
#create chain of charaters for HPC:
323+
chain_string = ""
324+
for element in Postprocess_models:
325+
chain_string += f'{element}.run>>'
326+
chain_string = chain_string.rstrip('>>')
327+
#add it to attribute 'Sequence':
328+
self.add_attribute_on_AML_element(PostprocessIE, 'Sequence')
329+
#add chain to attribute 'Sequence'
330+
self.write_value_on_AML_attribute(PostprocessIE, 'Sequence', chain_string)
331+
#create the phase internal elements
332+
for element in Postprocess_models:
333+
self.create_AML_element(PostprocessIE, element)
334+
#get the element just created
335+
ElementIE = PostprocessIE.find(f"./*[@Name='{element}']")
336+
#add ID attribute
337+
self.add_attribute_on_AML_element(ElementIE, self.Software_softwareID_name)
338+
#fill in ID attribute
339+
self.write_value_on_AML_attribute(ElementIE, self.Software_softwareID_name, element)
340+
#add Parameters IE
341+
self.create_AML_element(ElementIE, 'Parameters')
342+
#get Parameters IE
343+
ParametersIE = ElementIE.find("./*[@Name='Parameters']")
344+
#add output_file atribute to Parameters IE
345+
self.add_attribute_on_AML_element(ParametersIE, self.Software_Parameters_OutputFile_name)
346+
else:
347+
print("No postprocess")
348+
349+
# write inputs in Workflow_definition
350+
#find inputs IE
351+
InputsIE = Workflow_DefinitionIE.find(f"./*[@Name='{self.InputsIE_name}']")
352+
#find DoE
353+
DoEIE = InputsIE.find(f"./*[@Name='{self.DoEIE_name}']")
354+
#write DoE source in source attribute from the user's input
355+
self.write_value_on_AML_attribute(DoEIE, 'source', self.PathOfDoE)
356+
#write destination?
357+
358+
# write outputs in Workflow_definition
359+
OutputsIE = Workflow_DefinitionIE.find(f"./*[@Name='{self.OutputsIE_name}']")
360+
for output_element in self.filtered_selected_output_options:
361+
self.add_attribute_on_AML_element(OutputsIE, output_element)
362+
363+
# save all modifications
364+
roottemplate.save(self.template_instance_path)
365+
rootmaster.save(self.AML_master_path)
366+
367+
def save_AML(self):
368+
selected_output_options = [var.get() for var in self.selections[self.outputs_name]]
369+
self.filtered_selected_output_options = [item for item in selected_output_options if item.strip() != ""]
370+
371+
self.write_on_AML()
372+
373+
print('Data has been saved to the instance AutomationML file: {}'.format(self.template_instance_path))
374+
print('Data has been saved to the master AutomationML file: {}'.format(self.AML_master_path))
375+
376+
if __name__ == "__main__":
377+
root = tk.Tk()
378+
app = AML_GUI_App(root)
379+
root.mainloop()
380+
381+

0 commit comments

Comments
 (0)