ChimeraTK-ControlSystemAdapter-OPCUAAdapter  04.00.01
generatorForm.py
Go to the documentation of this file.
1 from PyQt5.QtWidgets import QFileDialog, QDialog, QMainWindow, QTreeWidgetItem, QCheckBox, QMessageBox, QDialogButtonBox, QComboBox, QHeaderView
2 from PyQt5.QtCore import Qt
3 from PyQt5.QtGui import QBrush, QColor
4 import os
5 from ctk_opcua_generator_tools.generatorUI import Ui_MainWindow
6 from ctk_opcua_generator_tools.encryptionUI import Ui_EncryptionDialog
7 from ctk_opcua_generator_tools.historyUI import Ui_HistoryDialog
8 from ctk_opcua_generator_tools.generatorClass import MapGenerator, XMLDirectory, XMLVar, EncryptionSettings, HistorySetting
9 import logging
10 from typing import Optional, List
11 
13  def _getFileDialog(self):
14  dlg = QFileDialog(self)
15  dlg.setFileMode(QFileDialog.ExistingFile)
16  dlg.setViewMode( QFileDialog.Detail )
17  dlg.setNameFilters( [self.tr('Certificate file (*.der)'), self.tr('All Files (*)')] )
18  dlg.setDefaultSuffix('der')
19  return dlg
20 
21  def openUserCert(self):
22  dlg = self._getFileDialog()
23  dlg.setWindowTitle('Set application certificate file')
24  # show dialog, save only if user did not cancel
25  if dlg.exec_() :
26  self.settings.certificate = str(dlg.selectedFiles()[0])
27  self.userCert.setText(self.settings.certificate)
28 
29  def openPrivateKey(self):
30  dlg = self._getFileDialog()
31  dlg.setWindowTitle('Set appliction private key file')
32  # show dialog, save only if user did not cancel
33  if dlg.exec_() :
34  self.settings.key = str(dlg.selectedFiles()[0])
35  self.privateKey.setText(self.settings.key)
36 
37  def _openDirectory(self) -> Optional[str]:
38  dlg = QFileDialog(self)
39  dlg.setFileMode(QFileDialog.Directory)
40  dlg.setOption(QFileDialog.ShowDirsOnly)
41  if dlg.exec() :
42  return str(dlg.selectedFiles()[0])
43  else:
44  return None
45 
46  def openTrustList(self):
47  directory = self._openDirectory()
48  if directory:
49  self.settings.trustList = directory
50  self.trustList.setText(directory)
51 
52  def openBlockList(self):
53  directory = self._openDirectory()
54  if directory:
55  self.settings.blockList = directory
56  self.blockList.setText(directory)
57 
58  def openIssuerList(self):
59  directory = self._openDirectory()
60  if directory:
61  self.settings.issuerList = directory
62  self.issuerList.setText(directory)
63 
64 
65  def __init__(self, data:EncryptionSettings, parent=None):
66  super(EncryptionDialog, self).__init__(parent)
67  self.setupUi(self)
68 
69  self.settings = data
70  if data.certificate:
71  self.userCert.setText(data.certificate)
72  if data.key:
73  self.privateKey.setText(data.key)
74  if data.trustList:
75  self.trustList.setText(data.trustList)
76  if data.blockList:
77  self.blockList.setText(data.blockList)
78  if data.issuerList:
79  self.issuerList.setText(data.issuerList)
80 
81  self.setUserCert.clicked.connect(self.openUserCert)
82  self.setPrivateKey.clicked.connect(self.openPrivateKey)
83  self.setBlockList.clicked.connect(self.openBlockList)
84  self.setTrustList.clicked.connect(self.openTrustList)
85  self.setIssuerList.clicked.connect(self.openIssuerList)
86 
88  def __init__(self, data:QComboBox, histories: List[HistorySetting], edit:bool, parent=None):
89  '''
90  @param data: The setting to be added.
91  @param histories: The list of all settings. Is used to ensure that names are unique!
92  '''
93  super(HistorySettingsDialog,self).__init__(parent)
94  self.setupUi(self)
95  self.data = data.currentData()
96  self.historyName.setText(self.data.name)
97  self.entriesPerResponse.setValue(self.data.entriesPerResponse)
98  self.bufferLength.setValue(self.data.bufferLength)
99  self.samplingInterval.setValue(self.data.interval)
100  self.histories = histories
101  self.edit = edit
102  # Save current text -> if edit is true and the name is changed we need to check again if it already exists
103  self.combo = data
104 
105  self.historyName.editingFinished.connect(self.checkNameExists)
106  self.samplingInterval.valueChanged.connect(self.updateData)
107  self.entriesPerResponse.valueChanged.connect(self.updateData)
108  self.bufferLength.valueChanged.connect(self.updateData)
109 
110  def checkNameExists(self):
111  # check if is new or name was changed
112  if self.edit == False or self.combo.currentText() != self.historyName.text():
113  found = next((x for x in self.histories if x.name == self.historyName.text()),None)
114  if found != None:
115  self.buttonBox.button( QDialogButtonBox.Ok ).setEnabled( False );
116  else:
117  self.buttonBox.button( QDialogButtonBox.Ok ).setEnabled( True );
118  self.data.name = self.historyName.text()
119  self.combo.setItemText(self.combo.currentIndex(),self.historyName.text())
120 
121  def updateData(self):
122  self.data.entriesPerResponse = self.entriesPerResponse.value()
123  self.data.interval = self.samplingInterval.value()
124  self.data.bufferLength = self.bufferLength.value()
125 
126 class MapGeneratorForm(QMainWindow, Ui_MainWindow):
127  def _createMapGenerator(self, fileName: str|None):
128  try:
129  self.MapGenerator: MapGenerator|None = MapGenerator(fileName)
130  if (fileName):
131  self.statusbar.showMessage(fileName)
132  self._fillTree()
133  except RuntimeError as error:
134  QMessageBox.critical(self, "Map file generator",
135  "Could not parse xml file. Is that a file generated by the XML generator of your application?\n Error: {}".format(error) )
136  self.treeWidget.clear()
137  self.MapGenerator = None
138  except:
139  QMessageBox.critical(self, "Map file generator",
140  "Could not parse xml file. Is that a file generated by the XML generator of your application?" )
141  self.treeWidget.clear()
142  self.MapGenerator = None
143 
144  def openFile(self, fromMenu:bool = True):
145  '''
146  Read all information from the given XML file.
147  Data is stored in the MapGenerator object.
148 
149  @param fromMenu: If true no user interaction box is shown, because we know the user wants to open a file.
150  '''
151  buttonReply = None
152  if not fromMenu:
153  buttonReply = QMessageBox.question(self, 'PyQt5 message', "You did not specify an input xml file (created with the XMLGenerator of your application).\nIf not you will only be able to set general OPC UA server properties without mapping features.\n Do you want to specify it now?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
154  if buttonReply == QMessageBox.Yes or fromMenu:
155  name = QFileDialog.getOpenFileName(self, caption='Set input file', filter="XML files (*.xml)", options=QFileDialog.DontResolveSymlinks)
156  if name[0]:
157  self._createMapGenerator(name[0])
158  else:
159  self._createMapGenerator(None)
160  logging.warning("No xml input file given.")
161 
162  def _fillConfig(self):
163  '''
164  Fill config information to the GUI fields.
165  '''
166  self._blockAndSetCheckbox(self.MapGenerator.addUnsecureEndpoint, self.addUnsecureEndpoint)
167  self._blockAndSetCheckbox(self.MapGenerator.encryptionEnabled, self.enableEncryptionButton)
168  self._blockAndSetCheckbox(self.MapGenerator.enableLogin, self.enableLoginSwitch)
169  self.configureEncryptionButton.setEnabled(self.MapGenerator.encryptionEnabled)
170  self.addUnsecureEndpoint.setEnabled(self.MapGenerator.encryptionEnabled)
171  self.userName.setEnabled(self.MapGenerator.enableLogin)
172  self.password.setEnabled(self.MapGenerator.enableLogin)
173  self._blockAndSetTextBox(self.MapGenerator.username, self.userName)
174  self._blockAndSetTextBox(self.MapGenerator.password, self.password)
175  # Attention the following fields are set in the GUI but not set in the mapping file as long as they
176  # are not changed by the user!
177  # Simply show what will be used, because root folder and application name default to the
178  # application name if not set in the mapping file!
179  self._blockAndSetTextBox(self.MapGenerator.applicationName, self.applicationName)
180  self._blockAndSetTextBox(self.MapGenerator.applicationDescription, self.applicationDescription)
181  self._bockAndSetValue(self.MapGenerator.port, self.port)
182  if self.rootFolder == None:
183  self._blockAndSetTextBox(self.MapGenerator.rootFolder, self.applicationName)
184  else:
185  self._blockAndSetTextBox(self.MapGenerator.rootFolder, self.rootFolder)
186  self.logLevelComboBox.blockSignals(True)
187  self.logLevelComboBox.setCurrentText(self.MapGenerator.logLevel)
188  self.logLevelComboBox.blockSignals(False)
189 
190  def _fillTree(self):
191  '''
192  Fill the tree by passing the root node read from the XML file.
193  Filling is done recursively.
194  '''
195  self.treeWidget.clear()
196  # block widget while filling in order to avoid calling _updateItem
197  self.treeWidget.blockSignals(True)
198  self._createDirectoryNode(self.treeWidget, self.MapGenerator.dir, True)
199  self.treeWidget.blockSignals(False)
200 
201  # resize columns
202  header = self.treeWidget.header()
203  #header.setSectionResizeMode(QHeaderView.ResizeToContents)
204  #header.setStretchLastSection(False)
205  header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
206  header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
207  header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
208  header.setSectionResizeMode(3, QHeaderView.ResizeToContents)
209  header.setSectionResizeMode(4, QHeaderView.Interactive)
210 
211  def _getCheckBox(self, item:XMLDirectory | XMLVar, text:str, node:QTreeWidgetItem):
212  '''
213  Create a checkbox.
214 
215  @param item: The XML items that corresponds to the checkbox.
216  @param text: The check box label.
217  @param node: The widget item where the checkbox will be added. It is assumed it is a QTreeWidgetItem with multiple columns.
218  '''
219  checkbox = QCheckBox(parent=self.treeWidget, text=text)
220  if item.exclude == True:
221  checkbox.setChecked(True)
222  if isinstance(item, XMLDirectory) == True:
223  checkbox.stateChanged.connect(lambda state: self._mapDirectory(state,item, node))
224  else:
225  checkbox.stateChanged.connect(lambda state: self._mapItem(state,item, node))
226  return checkbox
227 
228  def _getComboBox(self, item:XMLDirectory | XMLVar, node:QTreeWidgetItem):
229  '''
230  Create a combo box.
231 
232  @param item: The XML items that corresponds to the checkbox.
233  @param node: The widget item where the checkbox will be added. It is assumed it is a QTreeWidgetItem with multiple columns.
234  '''
235  combobox = QComboBox(parent=self.treeWidget)
236  combobox.addItem('No history', None)
237  combobox.currentIndexChanged.connect(lambda histIndex: self._setHistoryItem(histIndex, item, combobox, node))
238  combobox.setSizeAdjustPolicy(QComboBox.AdjustToContents)
239  return combobox
240 
241  def _updateItem(self, item:QTreeWidgetItem, index:int):
242  self.treeWidget.blockSignals(True)
243  data = item.data(0, Qt.UserRole)
244  if index == 0:
245  # Name
246  if item.text(index) == data.name:
247  data.newName = None
248  item.setForeground(index,QBrush(QColor("#000000")))
249  else:
250  data.newName = item.text(index)
251  item.setText(index, item.text(index) + " (Orig.: " + data.name + ")")
252  item.setForeground(index,QBrush(QColor("#FF0000")))
253  elif index == 3:
254  # Unit
255  if isinstance(data,XMLVar):
256  if item.text(index) == data.unit:
257  data.newUnit = None
258  item.setForeground(index,QBrush(QColor("#000000")))
259  else:
260  data.newUnit = item.text(index)
261  item.setText(index, item.text(index) + " (Orig.: " + str(data.unit or '') + ")")
262  item.setForeground(index,QBrush(QColor("#FF0000")))
263  else:
264  item.setText(index, '')
265  msg = "Adding unit to a directory does not make sense!"
266  QMessageBox.warning(self, "Map file generator", msg )
267  elif index == 4:
268  # Description
269  if item.text(index) == "":
270  data.newDescription = None
271  item.setForeground(index,QBrush(QColor("#000000")))
272  else:
273  data.newDescription = item.text(index)
274  item.setText(index, item.text(index) + " (Added descr.)")
275  item.setForeground(index,QBrush(QColor("#FF0000")))
276  elif index == 5:
277  # additional Location
278  if item.text(index) == "":
279  data.newDestination = None
280  item.setForeground(index,QBrush(QColor("#000000")))
281  else:
282  data.newDestination = item.text(index)
283  item.setText(5,data.newDestination)
284  item.setForeground(5,QBrush(QColor("#FF0000")))
285  self.treeWidget.blockSignals(False)
286 
287  def _setupRow(self, item:QTreeWidgetItem, data: XMLDirectory|XMLVar):
288  item.setData(0, Qt.UserRole, data)
289  self.treeWidget.setItemWidget(item, 1, self._getCheckBox(data, "exclude", item))
290  self.treeWidget.setItemWidget(item, 2, self._getComboBox(data, item))
291  if isinstance(data, XMLDirectory):
292  item.setFlags(Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled | Qt.ItemIsSelectable | Qt.ItemIsDropEnabled)
293  else:
294  item.setFlags(Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled | Qt.ItemIsSelectable)
295 
296  def _setState(self, state:int, item: XMLDirectory|XMLVar):
297  '''
298  Set data in XMLDirectory or XMLVar.
299  This is used to enable mapping.
300  '''
301  if state == Qt.Checked:
302  item.exclude = True
303  logging.debug("Excluding item {}.".format(item.name))
304  else:
305  item.exclude = False
306  logging.debug("Including item {}.".format(item.name))
307 
308  def _createVariableNode(self, parent:QTreeWidgetItem, v:XMLVar) -> QTreeWidgetItem:
309  '''
310  Create variable entry in the tree.
311  Reads description and unit.
312  Add CheckBox to enable/disable mapping of that variable.
313  '''
314  name = v.name
315  if v.newName != None:
316  name = "{} (Orig.: {})".format(v.newName, v.name)
317  description = v.description
318  if v.newDescription != None:
319  description = "{} (Orig.: {})".format(v.newDescription, v.description)
320  unit = v.unit
321  if v.newUnit != None:
322  unit = "{} (Orig.: {})".format(v.newUnit, v.unit)
323  node = QTreeWidgetItem(parent, [name]+[""]*2 + [unit] + [description]+[v.newDestination])
324 
325  if v.direction == 'control_system_to_application':
326  node.setBackground(0,QBrush(QColor("#fee8c8")))
327  else:
328  node.setBackground(0,QBrush(QColor("#e5f5e0")))
329 
330  if v.newName != None:
331  node.setForeground(0,QBrush(QColor("#FF0000")))
332  if v.newUnit != None:
333  node.setForeground(3,QBrush(QColor("#FF0000")))
334  if v.newDescription != None:
335  node.setForeground(4,QBrush(QColor("#FF0000")))
336  if v.newDestination != None:
337  node.setForeground(5,QBrush(QColor("#FF0000")))
338  self._setupRow(node, v)
339  return node
340 
341  def _createDirectoryNode(self, parent:QTreeWidgetItem, directory:XMLDirectory, isRootNode:bool = False) -> QTreeWidgetItem:
342  '''
343  Create a directory entry in the Tree.
344  This will recursively add sub directories and variables in the directories.
345  '''
346  name = directory.name
347  if directory.newName != None:
348  name = "{} (Orig.: {})".format(directory.newName, directory.name)
349  description = ''
350  if directory.newDescription != None:
351  description = "{} (Added descr.)".format(directory.newDescription)
352  mainNode = QTreeWidgetItem(parent, [name] + [""]*3 + [description] + [directory.newDestination or ''])
353  if directory.newName != None:
354  mainNode.setForeground(0,QBrush(QColor("#FF0000")))
355  if directory.newDescription != None:
356  mainNode.setForeground(4,QBrush(QColor("#FF0000")))
357  if directory.newDestination != None:
358  mainNode.setForeground(5,QBrush(QColor("#FF0000")))
359 
360  self._setupRow(mainNode, directory)
361  if isRootNode == True:
362  mainNode.setExpanded(True)
363  for d in directory.dirs:
364  self._createDirectoryNode(mainNode, d)
365  for v in directory.vars:
366  self._createVariableNode(mainNode, v)
367  return mainNode
368 
369  def _mapItem(self, state:int, var:XMLVar, node:QTreeWidgetItem):
370  '''
371  Called when a variable is to be mapped.
372  The corresponding check box is edited.
373  '''
374  self._setState(state, var)
375  logging.debug("Childs: {}".format(node.childCount()))
376 
377  def _mapDirectory(self, state:int, directory:XMLDirectory, node:QTreeWidgetItem):
378  '''
379  Called when a directory is to be mapped.
380  The corresponding check box is edited.
381  This will change the map status of all variables in the directory.
382  '''
383  logging.debug("Mapping directory.")
384  for chId in range(node.childCount()):
385  ch = node.child(chId)
386  self.treeWidget.itemWidget(ch, 1).setCheckState(state)
387  self._setState(state, directory)
388 
389  def _setHistoryItem(self, histIndex:int, var:XMLVar|XMLDirectory, combo:QComboBox, node:QTreeWidgetItem):
390  '''
391  Called when historizing setting is choosen.
392  @param histIndex: The index of the history setting.
393  '''
394  if combo.itemText(histIndex) != 'No history':
395  var.historizing = combo.itemText(histIndex)
396  else:
397  var.historizing = None
398 
399  if isinstance(var, XMLDirectory):
400  for chId in range(node.childCount()):
401  ch = node.child(chId)
402  self.treeWidget.itemWidget(ch, 2).setCurrentText(combo.itemText(histIndex))
403  if var.historizing:
404  self.treeWidget.itemWidget(ch, 2).setEnabled(False)
405  else:
406  self.treeWidget.itemWidget(ch, 2).setEnabled(True)
407 
408  def closeEvent(self, event):
409  msg = "Are you sure you want to close the editor?"
410  reply = QMessageBox.question(self, 'ChimeraTK Logical Name Mapping editor', msg, QMessageBox.Yes, QMessageBox.No)
411  if reply != QMessageBox.Yes:
412  event.ignore()
413  else:
414  event.accept()
415 
416  def saveFile(self):
417  '''
418  Save file. If file not yet set open dialog with saveFileAs()
419  '''
420  try:
421  if self.MapGenerator.outputFile == None:
422  self.saveFileAs()
423  else:
424  self.MapGenerator.save()
425  except RuntimeError as error:
426  msg = "The following error was reported: " + str(error)
427  QMessageBox.critical(self, "Map file generator", msg )
428 
429  def saveFileAs(self):
430  # create file-save dialog
431  dlg = QFileDialog(self, directory=self.MapGenerator.applicationName + "_mapping.xml")
432  dlg.setAcceptMode(QFileDialog.AcceptSave)
433  dlg.setWindowTitle('Save XML mapping file')
434  dlg.setViewMode( QFileDialog.Detail )
435  dlg.setNameFilters( [self.tr('XML Mapping Files (*.xml)'), self.tr('All Files (*)')] )
436  dlg.setDefaultSuffix('xml')
437  if self.MapGenerator.outputFile != None:
438  dlg.setDirectory( os.path.dirname(self.MapGenerator.outputFile) )
439 
440  # show dialog, save only if user did not cancel
441  if dlg.exec_() :
442  # file name must be converted into standard python string
443  self.MapGenerator.outputFile = str(dlg.selectedFiles()[0])
444  # save the file
445  self.saveFile()
446  self.actionSave.setEnabled(True)
447 
448  def enableLogin(self, state):
449  if state == Qt.Checked:
450  self.userName.setEnabled(True)
451  self.password.setEnabled(True)
452  self.MapGenerator.enableLogin = True
453  else:
454  self.userName.setEnabled(False)
455  self.password.setEnabled(False)
456  self.MapGenerator.enableLogin = False
457 
458  def updateConfig(self):
459  self.MapGenerator.username = self.userName.text()
460  self.MapGenerator.password = self.password.text()
461  self.MapGenerator.applicationName = self.applicationName.text()
462  self.MapGenerator.rootFolder = self.rootFolder.text()
463  self.MapGenerator.applicationDescription = self.applicationDescription.text()
464  self.MapGenerator.port = self.port.value()
465  self.MapGenerator.logLevel = self.logLevelComboBox.currentText()
466 
467 
468  def dropEvent(self, event):
469  # get item where to drop
470  item = self.treeWidget.itemAt(event.pos())
471  if isinstance(item.data(0,Qt.UserRole), XMLVar):
472  msg = "Variables or directories can only be moved to other directories."
473  QMessageBox.critical(self, "Map file generator", msg )
474  return
475  # get item that is dropped
476  current = self.treeWidget.currentItem()
477  # get data that is dropped
478  currentData = current.data(0,Qt.UserRole)
479  currentData.newDestination = item.data(0,Qt.UserRole).path
480  current.setText(5,currentData.newDestination)
481  current.setForeground(5,QBrush(QColor("#FF0000")))
482  # create new item in the dropped place
483  # self._createVariableNode(item, currentData)
484  # remove original item -> leave original PV/directory
485  # parent = current.parent()
486  # parent.removeChild(current)
487 
488  def dragEnterEvent(self, e)->None:
489  e.accept()
490 
492  dlg = EncryptionDialog(parent=self, data=self.MapGenerator)
493  dlg.exec()
494 
496  states = [self.enableEncryptionButton.isChecked(),
497  self.addUnsecureEndpoint.isChecked()]
498  self.addUnsecureEndpoint.setEnabled(states[0])
499  self.configureEncryptionButton.setEnabled(states[0])
500  self.MapGenerator.encryptionEnabled = states[0]
501  self.MapGenerator.addUnsecureEndpoint = states[1]
502 
504  histName = 'historySetting'
505  if self.histories.findText(histName) != -1:
506  i = 0
507  histName = 'historySetting_{}'.format(i)
508  while(self.histories.findText(histName) != -1):
509  histName = 'historySetting_{}'.format(i)
510  i = i+1
511  setting = HistorySetting(name=histName)
512  # add item to the historizing combo box
513  self.histories.addItem(setting.name, setting)
514  self.histories.setCurrentText(setting.name)
515  dlg = HistorySettingsDialog(parent=self, data=self.histories, histories=self.MapGenerator.historySettings, edit=False)
516  dlg.exec()
517  self.addHistorySetting(setting)
518 
519  def addHistorySetting(self, setting: HistorySetting, updateMapGenerator: bool = True):
520  '''
521  @param setting: The Setting to add.
522  @param updateMapGenerator: If False the setting is not added to the mapGenerator list.
523  This option is used when an existing map file was read and here
524  only the combo box should be updated.
525  '''
526  logging.info("Adding historizing setting with name: {}".format(setting.name))
527  # add HistorySetting to the settings of the MapGenerator
528  if updateMapGenerator:
529  self.MapGenerator.historySettings.append(setting)
530  # add history settings to the combo boxes in the TreeWidget (recursively)
531  self.addComboEntry(self.treeWidget.itemAt(0,0), setting)
532  header = self.treeWidget.header()
533  # resize the history column accoding to the new historizing setting name, size of 200 is ignored size
534  # the size is set to the combobox size using ResizeToContents-> see fillTree()
535  header.resizeSection(2,200)
536  self.editHistorySettingButton.setEnabled(True)
537  self.setHistoryForInputsButton.setEnabled(True)
538 
539  def addComboEntry(self, item:QTreeWidgetItem, setting:HistorySetting):
540  self.treeWidget.itemWidget(item, 2).addItem(setting.name, setting)
541  for chId in range(item.childCount()):
542  self.addComboEntry(item.child(chId), setting)
543 
545  dlg = HistorySettingsDialog(parent=self, data=self.histories,histories=self.MapGenerator.historySettings, edit=True)
546  dlg.exec()
547 
548  def updateHistories(self, item:QTreeWidgetItem):
549  '''
550  After reading an existing map file the tree needs to be updated.
551  The information which history to use is already in the Item data.
552  '''
553  data = item.data(0, Qt.UserRole)
554  if isinstance(data, XMLVar):
555  if data.historizing != None:
556  self.treeWidget.itemWidget(item, 2).setCurrentText(data.historizing)
557  elif isinstance(data, XMLDirectory):
558  if data.historizing != None:
559  # if the directory has history set the history -> child will be set automatically by _setHistoryItem
560  self.treeWidget.itemWidget(item, 2).setCurrentText(data.historizing)
561  else:
562  # Check childs for history settings
563  for chId in range(item.childCount()):
564  self.updateHistories(item.child(chId))
565 
566  def addHistoryForInputs(self, isChecked:bool, node:QTreeWidgetItem):
567  data = node.data(0, Qt.UserRole)
568  if isinstance(data, XMLVar) and data.direction == 'control_system_to_application':
569  if isChecked:
570  self.treeWidget.itemWidget(node, 2).setCurrentText(self.histories.currentText())
571  else:
572  self.treeWidget.itemWidget(node, 2).setCurrentText('No history')
573 
574  else:
575  for chId in range(node.childCount()):
576  self.addHistoryForInputs(isChecked,node.child(chId))
577 
578  def _blockAndSetTextBox(self, value: str, control):
579  '''
580  Set a text box without updating the configuration,
581  which would override parameters of the MapGenerator.
582  '''
583  control.blockSignals(True)
584  if value != None:
585  control.setText(value)
586  control.blockSignals(False)
587 
588  def _bockAndSetValue(self, value: int, control):
589  '''
590  Set value of spinbox.
591  '''
592  control.blockSignals(True)
593  if value != None:
594  control.setValue(value)
595  control.blockSignals(False)
596 
597  def _blockAndSetCheckbox(self, value: bool, control):
598  '''
599  Set a check box without updating the configuration,
600  which would override parameters of the MapGenerator.
601  '''
602  control.blockSignals(True)
603  if value == True:
604  control.setChecked(True)
605  else:
606  control.setChecked(False)
607  control.blockSignals(False)
608 
609  def loadMapFile(self):
610  if self.MapGenerator == None:
611  msg = "You have to open a xml file first."
612  QMessageBox.critical(self, "Map file generator", msg )
613  else:
614  name = QFileDialog.getOpenFileName(self, caption='Load existing mapping file', filter="XML files (*.xml)", options=QFileDialog.DontResolveSymlinks)
615  if name[0]:
616  try:
617  missed = self.MapGenerator.parseMapFile(name[0])
618  if missed[0] != 0 or missed[1] != 0 or missed[2] != 0:
619  QMessageBox.warning(self, "Map file generator",
620  "{} directories, {} PV and {} exclude paths could not be found by their source name in the original variable tree.\n Probably"
621  " the mapping file does not correspong to the application XML file or the application was changed in the mean time.".format(missed[0], missed[1], missed[2]))
622  self._fillConfig()
623  if self.MapGenerator.dir == None:
624  return
625  # update the tree
626  self._fillTree()
627  # update historizing settings
628  for setting in self.MapGenerator.historySettings:
629  if self.histories.findText(setting.name) == -1:
630  self.histories.addItem(setting.name,setting)
631  self.addHistorySetting(setting=setting, updateMapGenerator=False)
632  else:
633  logging.warning("Setting with name {} already exist. Not loading parameters from mapping file.".format(setting.name))
634  # loop over tree and apply history settings
635  self.updateHistories(self.treeWidget.itemAt(0,0))
636 
637  except RuntimeError as error:
638  QMessageBox.critical(self, "Map file generator",
639  "Failed to load map file: {}\n Error: {}".format(name[0], error) )
640 
641  def __init__(self, args, parent=None):
642  super(MapGeneratorForm, self).__init__(parent)
643  self.setupUi(self)
644 
645 
646  self.MapGenerator = None
647  if args.input:
648  self._createMapGenerator(args.input)
649  elif args.noinput:
650  self.MapGenerator: MapGenerator|None = MapGenerator(None)
651  else:
652  self.openFile(fromMenu=False)
653 
654  self.histories.setSizeAdjustPolicy(QComboBox.AdjustToContents)
655 
656  self.treeWidget.itemChanged.connect(self._updateItem)
657 
658  self.actionOpen_File.triggered.connect(lambda: self.openFile(fromMenu=True))
659  self.actionSave_as.triggered.connect(self.saveFileAs)
660  self.actionSave.triggered.connect(self.saveFile)
661  self.actionLoad_mapping_file.triggered.connect(self.loadMapFile)
662  self.actionQuit.triggered.connect(self.close)
663 
664  self.enableLoginSwitch.stateChanged.connect(self.enableLogin)
665  self.password.textChanged.connect(self.updateConfig)
666  self.userName.textChanged.connect(self.updateConfig)
667  self.applicationName.textChanged.connect(self.updateConfig)
668  self.rootFolder.textChanged.connect(self.updateConfig)
669  self.port.valueChanged.connect(self.updateConfig)
670  self.applicationDescription.textChanged.connect(self.updateConfig)
671  self.logLevelComboBox.currentTextChanged.connect(self.updateConfig)
672  self.enableEncryptionButton.stateChanged.connect(self.updateEncryptionConfiguration)
673  self.addUnsecureEndpoint.stateChanged.connect(self.updateEncryptionConfiguration)
674  self.configureEncryptionButton.clicked.connect(self.configureEncryption)
675  self.editHistorySettingButton.clicked.connect(self.editHistorySetting)
676  self.addHistorySettingButton.clicked.connect(self.prepareNewHistorySetting)
677  self.setHistoryForInputsButton.clicked.connect(lambda isChecked: self.addHistoryForInputs(isChecked, self.treeWidget.itemAt(0,0)))
678 
679 
680  # Allow to move items
681  self.treeWidget.dropEvent = self.dropEvent
682  self.treeWidget.dragEnterEvent = self.dragEnterEvent
ctk_opcua_generator_tools.generatorForm.HistorySettingsDialog.combo
combo
Definition: generatorForm.py:103
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.applicationDescription
applicationDescription
Definition: generatorUI.py:67
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.password
password
Definition: generatorUI.py:109
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.addUnsecureEndpoint
addUnsecureEndpoint
Definition: generatorUI.py:144
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.editHistorySetting
def editHistorySetting(self)
Definition: generatorForm.py:544
ctk_opcua_generator_tools.encryptionUI.Ui_EncryptionDialog
Definition: encryptionUI.py:14
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._getCheckBox
def _getCheckBox(self, XMLDirectory|XMLVar item, str text, QTreeWidgetItem node)
Definition: generatorForm.py:211
ctk_opcua_generator_tools.generatorForm.HistorySettingsDialog.data
data
Definition: generatorForm.py:95
ctk_opcua_generator_tools.generatorForm.HistorySettingsDialog.histories
histories
Definition: generatorForm.py:100
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._blockAndSetTextBox
def _blockAndSetTextBox(self, str value, control)
Definition: generatorForm.py:578
ctk_opcua_generator_tools.generatorClass.HistorySetting
Definition: generatorClass.py:15
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.addHistoryForInputs
def addHistoryForInputs(self, bool isChecked, QTreeWidgetItem node)
Definition: generatorForm.py:566
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._createVariableNode
QTreeWidgetItem _createVariableNode(self, QTreeWidgetItem parent, XMLVar v)
Definition: generatorForm.py:308
testMapGenerator.format
format
Definition: testMapGenerator.py:78
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.configureEncryptionButton
configureEncryptionButton
Definition: generatorUI.py:148
ctk_opcua_generator_tools.historyUI.Ui_HistoryDialog.historyName
historyName
Definition: historyUI.py:27
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.setHistoryForInputsButton
setHistoryForInputsButton
Definition: generatorUI.py:87
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.dropEvent
def dropEvent(self, event)
Definition: generatorForm.py:468
ctk_opcua_generator_tools.historyUI.Ui_HistoryDialog.buttonBox
buttonBox
Definition: historyUI.py:85
ctk_opcua_generator_tools.generatorForm.EncryptionDialog.openBlockList
def openBlockList(self)
Definition: generatorForm.py:52
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.userName
userName
Definition: generatorUI.py:99
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.setupUi
def setupUi(self, MainWindow)
Definition: generatorUI.py:15
ctk_opcua_generator_tools.historyUI.Ui_HistoryDialog.bufferLength
bufferLength
Definition: historyUI.py:56
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._mapItem
def _mapItem(self, int state, XMLVar var, QTreeWidgetItem node)
Definition: generatorForm.py:369
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.histories
histories
Definition: generatorUI.py:78
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.updateHistories
def updateHistories(self, QTreeWidgetItem item)
Definition: generatorForm.py:548
ctk_opcua_generator_tools.generatorForm.EncryptionDialog._getFileDialog
def _getFileDialog(self)
Definition: generatorForm.py:13
ctk_opcua_generator_tools.encryptionUI.Ui_EncryptionDialog.blockList
blockList
Definition: encryptionUI.py:96
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._setHistoryItem
def _setHistoryItem(self, int histIndex, XMLVar|XMLDirectory var, QComboBox combo, QTreeWidgetItem node)
Definition: generatorForm.py:389
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.port
port
Definition: generatorUI.py:43
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.rootFolder
rootFolder
Definition: generatorForm.py:182
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.logLevelComboBox
logLevelComboBox
Definition: generatorUI.py:52
ctk_opcua_generator_tools.generatorForm.EncryptionDialog.openIssuerList
def openIssuerList(self)
Definition: generatorForm.py:58
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.actionOpen_File
actionOpen_File
Definition: generatorUI.py:173
ctk_opcua_generator_tools.encryptionUI.Ui_EncryptionDialog.issuerList
issuerList
Definition: encryptionUI.py:113
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm
Definition: generatorForm.py:126
ctk_opcua_generator_tools.generatorForm.EncryptionDialog._openDirectory
Optional[str] _openDirectory(self)
Definition: generatorForm.py:37
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._setupRow
def _setupRow(self, QTreeWidgetItem item, XMLDirectory|XMLVar data)
Definition: generatorForm.py:287
ctk_opcua_generator_tools.historyUI.Ui_HistoryDialog.setupUi
def setupUi(self, HistoryDialog)
Definition: historyUI.py:15
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.editHistorySettingButton
editHistorySettingButton
Definition: generatorUI.py:83
ctk_opcua_generator_tools.generatorForm.EncryptionDialog.settings
settings
Definition: generatorForm.py:69
ctk_opcua_generator_tools.generatorClass
Definition: generatorClass.py:1
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._createDirectoryNode
QTreeWidgetItem _createDirectoryNode(self, QTreeWidgetItem parent, XMLDirectory directory, bool isRootNode=False)
Definition: generatorForm.py:341
ctk_opcua_generator_tools.encryptionUI.Ui_EncryptionDialog.setPrivateKey
setPrivateKey
Definition: encryptionUI.py:60
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.statusbar
statusbar
Definition: generatorUI.py:170
ctk_opcua_generator_tools.generatorClass.MapGenerator
Definition: generatorClass.py:428
ctk_opcua_generator_tools.generatorForm.HistorySettingsDialog
Definition: generatorForm.py:87
ctk_opcua_generator_tools.generatorForm.HistorySettingsDialog.edit
edit
Definition: generatorForm.py:101
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._fillTree
def _fillTree(self)
Definition: generatorForm.py:190
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._blockAndSetCheckbox
def _blockAndSetCheckbox(self, bool value, control)
Definition: generatorForm.py:597
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.prepareNewHistorySetting
def prepareNewHistorySetting(self)
Definition: generatorForm.py:503
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.updateEncryptionConfiguration
def updateEncryptionConfiguration(self, state)
Definition: generatorForm.py:495
ctk_opcua_generator_tools.generatorForm.EncryptionDialog.__init__
def __init__(self, EncryptionSettings data, parent=None)
Definition: generatorForm.py:65
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.addHistorySetting
def addHistorySetting(self, HistorySetting setting, bool updateMapGenerator=True)
Definition: generatorForm.py:519
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.saveFileAs
def saveFileAs(self)
Definition: generatorForm.py:429
str
#define str(a)
Definition: ua_adapter.cpp:52
ctk_opcua_generator_tools.generatorForm.EncryptionDialog.openPrivateKey
def openPrivateKey(self)
Definition: generatorForm.py:29
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.actionQuit
actionQuit
Definition: generatorUI.py:175
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.actionLoad_mapping_file
actionLoad_mapping_file
Definition: generatorUI.py:183
ctk_opcua_generator_tools.encryptionUI.Ui_EncryptionDialog.setUserCert
setUserCert
Definition: encryptionUI.py:42
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.MapGenerator
MapGenerator
Definition: generatorForm.py:137
ctk_opcua_generator_tools.encryptionUI.Ui_EncryptionDialog.setTrustList
setTrustList
Definition: encryptionUI.py:85
ctk_opcua_generator_tools.generatorUI
Definition: generatorUI.py:1
ctk_opcua_generator_tools.generatorForm.HistorySettingsDialog.updateData
def updateData(self)
Definition: generatorForm.py:121
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._mapDirectory
def _mapDirectory(self, int state, XMLDirectory directory, QTreeWidgetItem node)
Definition: generatorForm.py:377
ctk_opcua_generator_tools.encryptionUI
Definition: encryptionUI.py:1
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow
Definition: generatorUI.py:14
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._createMapGenerator
def _createMapGenerator(self, str|None fileName)
Definition: generatorForm.py:127
ctk_opcua_generator_tools.historyUI
Definition: historyUI.py:1
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._getComboBox
def _getComboBox(self, XMLDirectory|XMLVar item, QTreeWidgetItem node)
Definition: generatorForm.py:228
ctk_opcua_generator_tools.encryptionUI.Ui_EncryptionDialog.privateKey
privateKey
Definition: encryptionUI.py:53
ctk_opcua_generator_tools.encryptionUI.Ui_EncryptionDialog.setIssuerList
setIssuerList
Definition: encryptionUI.py:119
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._bockAndSetValue
def _bockAndSetValue(self, int value, control)
Definition: generatorForm.py:588
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.openFile
def openFile(self, bool fromMenu=True)
Definition: generatorForm.py:144
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.updateConfig
def updateConfig(self)
Definition: generatorForm.py:458
ctk_opcua_generator_tools.generatorForm.HistorySettingsDialog.checkNameExists
def checkNameExists(self)
Definition: generatorForm.py:110
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.addComboEntry
def addComboEntry(self, QTreeWidgetItem item, HistorySetting setting)
Definition: generatorForm.py:539
ctk_opcua_generator_tools.historyUI.Ui_HistoryDialog
Definition: historyUI.py:14
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._fillConfig
def _fillConfig(self)
Definition: generatorForm.py:162
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.loadMapFile
def loadMapFile(self)
Definition: generatorForm.py:609
ctk_opcua_generator_tools.encryptionUI.Ui_EncryptionDialog.setBlockList
setBlockList
Definition: encryptionUI.py:102
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._setState
def _setState(self, int state, XMLDirectory|XMLVar item)
Definition: generatorForm.py:296
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.enableEncryptionButton
enableEncryptionButton
Definition: generatorUI.py:141
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.__init__
def __init__(self, args, parent=None)
Definition: generatorForm.py:641
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.treeWidget
treeWidget
Definition: generatorUI.py:153
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.actionSave_as
actionSave_as
Definition: generatorUI.py:178
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.actionSave
actionSave
Definition: generatorUI.py:180
ctk_opcua_generator_tools.encryptionUI.Ui_EncryptionDialog.setupUi
def setupUi(self, EncryptionDialog)
Definition: encryptionUI.py:15
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.saveFile
def saveFile(self)
Definition: generatorForm.py:416
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.enableLogin
def enableLogin(self, state)
Definition: generatorForm.py:448
ctk_opcua_generator_tools.encryptionUI.Ui_EncryptionDialog.trustList
trustList
Definition: encryptionUI.py:79
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.applicationName
applicationName
Definition: generatorUI.py:35
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.enableLoginSwitch
enableLoginSwitch
Definition: generatorUI.py:93
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm._updateItem
def _updateItem(self, QTreeWidgetItem item, int index)
Definition: generatorForm.py:241
ctk_opcua_generator_tools.generatorForm.HistorySettingsDialog.__init__
def __init__(self, QComboBox data, List[HistorySetting] histories, bool edit, parent=None)
Definition: generatorForm.py:88
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.configureEncryption
def configureEncryption(self)
Definition: generatorForm.py:491
ctk_opcua_generator_tools.generatorUI.Ui_MainWindow.addHistorySettingButton
addHistorySettingButton
Definition: generatorUI.py:75
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.closeEvent
def closeEvent(self, event)
Definition: generatorForm.py:408
ctk_opcua_generator_tools.encryptionUI.Ui_EncryptionDialog.userCert
userCert
Definition: encryptionUI.py:35
ctk_opcua_generator_tools.historyUI.Ui_HistoryDialog.samplingInterval
samplingInterval
Definition: historyUI.py:73
ctk_opcua_generator_tools.historyUI.Ui_HistoryDialog.entriesPerResponse
entriesPerResponse
Definition: historyUI.py:40
ctk_opcua_generator_tools.generatorForm.EncryptionDialog.openUserCert
def openUserCert(self)
Definition: generatorForm.py:21
ctk_opcua_generator_tools.generatorForm.MapGeneratorForm.dragEnterEvent
None dragEnterEvent(self, e)
Definition: generatorForm.py:488
ctk_opcua_generator_tools.generatorForm.EncryptionDialog.openTrustList
def openTrustList(self)
Definition: generatorForm.py:46
ctk_opcua_generator_tools.generatorForm.EncryptionDialog
Definition: generatorForm.py:12