123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- # -*- coding: utf-8 -*-
- #
- # AWL simulator - GUI standard library window
- #
- # Copyright 2014-2018 Michael Buesch <m@bues.ch>
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License along
- # with this program; if not, write to the Free Software Foundation, Inc.,
- # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- #
- from __future__ import division, absolute_import, print_function, unicode_literals
- #from awlsim.common.cython_support cimport * #@cy
- from awlsim.common.compat import *
- from awlsim.gui.util import *
- from awlsim.gui.icons import *
- from awlsim.core.systemblocks.system_sfc import *
- from awlsim.core.systemblocks.system_sfb import *
- from awlsim.core.systemblocks.tables import SFC_table, SFB_table
- from awlsim.library.libentry import *
- from awlsim.library.libselection import *
- class GenericActionWidget(QWidget):
- # Signal: Code paste request.
- paste = Signal(str)
- # Signal: Add a symbol to the symbol table
- # Arguments: symbolName, address, dataType, comment
- addSymbol = Signal(str, str, str, str)
- # Signal: Add library selection
- addLibrary = Signal(AwlLibEntrySelection)
- # Signal: Finish the library selection
- finish = Signal()
- def __init__(self, parent=None):
- QWidget.__init__(self, parent)
- self.setLayout(QGridLayout())
- self.layout().setContentsMargins(QMargins(5, 0, 5, 0))
- def _pasteCallGeneric(self, targetName, needDB, interfaceFields):
- fields = []
- for ftype in (BlockInterfaceField.FTYPE_IN,
- BlockInterfaceField.FTYPE_OUT,
- BlockInterfaceField.FTYPE_INOUT):
- with contextlib.suppress(KeyError):
- fields.extend(interfaceFields[ftype])
- ret = [ "CALL %s%s%s" %\
- (targetName,
- ", DB ..." if needDB else "",
- " (" if fields else "")
- ]
- for field in fields:
- ret.append("\t%s := ...\t, // %s" %\
- (field.name, str(field.dataType)))
- if fields:
- ret.append(")")
- ret.append("")
- self.paste.emit("\n".join(ret))
- def _blockToInterfaceText(self, blockIdent, blockSym,
- verboseText,
- interfaceFields):
- desc = [ "%s \"%s\"" % (blockIdent, blockSym) ]
- if verboseText:
- desc.append(verboseText)
- desc.append("")
- for ftype, fname in ((BlockInterfaceField.FTYPE_IN, "VAR_INPUT"),
- (BlockInterfaceField.FTYPE_OUT, "VAR_OUTPUT"),
- (BlockInterfaceField.FTYPE_INOUT, "VAR_IN_OUT")):
- try:
- fields = interfaceFields[ftype]
- except KeyError:
- continue
- if not fields:
- continue
- desc.append(" " + fname)
- for field in fields:
- field.fieldType = ftype
- desc.append(" %s" % field.varDeclString())
- return "\n".join(desc)
- def defaultPaste(self):
- pass
- class SysActionWidget(GenericActionWidget):
- def __init__(self, parent=None):
- GenericActionWidget.__init__(self, parent)
- self.systemBlockCls = None
- self.blockPrefix = None
- self.desc = QLabel(self)
- self.desc.setFont(getDefaultFixedFont())
- self.layout().addWidget(self.desc, 0, 0, 1, 2)
- self.layout().setRowStretch(1, 1)
- label = QLabel("Paste at cursor position:", self)
- self.layout().addWidget(label, 2, 0, 1, 2)
- self.pasteCallSymButton = QPushButton(self)
- self.layout().addWidget(self.pasteCallSymButton, 3, 0)
- self.pasteCallButton = QPushButton(self)
- self.layout().addWidget(self.pasteCallButton, 3, 1)
- self.pasteCallSymButton.released.connect(self.__pasteCallSym)
- self.pasteCallButton.released.connect(self.__pasteCall)
- def updateData(self, prefix, systemBlockCls):
- self.systemBlockCls = systemBlockCls
- self.blockPrefix = prefix
- blockNumber, symbolName, blockDesc = systemBlockCls.name
- desc = self._blockToInterfaceText("%s %d" % (prefix, blockNumber),
- symbolName, blockDesc,
- systemBlockCls.interfaceFields)
- self.desc.setText(desc)
- self.pasteCallButton.setText("CALL %s %d" %\
- (prefix, blockNumber))
- self.pasteCallSymButton.setText("CALL \"%s\"" % symbolName)
- def __pasteCall(self):
- blockNumber, symbolName, blockDesc = self.systemBlockCls.name
- self._pasteCallGeneric("%s %d" % (self.blockPrefix, blockNumber),
- self.systemBlockCls._isFB,
- self.systemBlockCls.interfaceFields)
- self.finish.emit()
- def __pasteCallSym(self):
- blockNumber, symbolName, blockDesc = self.systemBlockCls.name
- self._pasteCallGeneric('"%s"' % symbolName,
- self.systemBlockCls._isFB,
- self.systemBlockCls.interfaceFields)
- self.addSymbol.emit(symbolName,
- "%s %s" % (self.blockPrefix, blockNumber),
- "%s %s" % (self.blockPrefix, blockNumber),
- blockDesc)
- self.finish.emit()
- def defaultPaste(self):
- self.__pasteCallSym()
- class LibActionWidget(GenericActionWidget):
- def __init__(self, parent=None):
- GenericActionWidget.__init__(self, parent)
- self.libEntryCls = None
- self.desc = QLabel(self)
- self.desc.setFont(getDefaultFixedFont())
- self.layout().addWidget(self.desc, 0, 0, 1, 2)
- self.layout().setRowStretch(1, 1)
- label = QLabel("Paste at cursor position:", self)
- self.layout().addWidget(label, 2, 0, 1, 2)
- self.pasteCallSymButton = QPushButton(self)
- self.layout().addWidget(self.pasteCallSymButton, 3, 0)
- self.pasteCallButton = QPushButton(self)
- self.layout().addWidget(self.pasteCallButton, 3, 1)
- self.pasteCodeSymButton = QPushButton(self)
- self.layout().addWidget(self.pasteCodeSymButton, 4, 0)
- self.pasteCodeButton = QPushButton(self)
- self.layout().addWidget(self.pasteCodeButton, 4, 1)
- self.pasteCallSymButton.released.connect(self.__pasteCallSym)
- self.pasteCallButton.released.connect(self.__pasteCall)
- self.pasteCodeSymButton.released.connect(self.__pasteCodeSym)
- self.pasteCodeButton.released.connect(self.__pasteCode)
- def updateData(self, libEntryCls):
- self.libEntryCls = libEntryCls
- prefix = "FC" if libEntryCls._isFC else "FB"
- typeStr = "FUNCTION" if libEntryCls._isFC else "FUNCTION_BLOCK"
- self.blockName = "%s %d" % (prefix, libEntryCls.staticIndex)
- desc = self._blockToInterfaceText(self.blockName,
- libEntryCls.symbolName,
- libEntryCls.description,
- libEntryCls.interfaceFields)
- self.desc.setText(desc)
- self.pasteCodeButton.setText("%s %s" %\
- (typeStr, self.blockName))
- self.pasteCodeSymButton.setText("%s \"%s\"" %\
- (typeStr, libEntryCls.symbolName))
- self.pasteCallButton.setText("CALL %s" % self.blockName)
- self.pasteCallSymButton.setText("CALL \"%s\"" %\
- libEntryCls.symbolName)
- def __pasteCodeWarning(self):
- res = QMessageBox.warning(self,
- "Paste library code body?",
- "Warning: It is not recommended to paste library "
- "code into the project sources. You should instead "
- "just import the library (via library selection table) "
- "and CALL the imported function.\n\n"
- "See the 'CALL \"%s\"' or 'CALL %s' buttons.\n\n"
- "Do you want to paste the code nevertheless?" % (
- self.libEntryCls.symbolName, self.blockName),
- QMessageBox.Yes | QMessageBox.No,
- QMessageBox.No)
- return res == QMessageBox.Yes
- def __pasteCode(self):
- if not self.__pasteCodeWarning():
- return
- self.paste.emit(self.libEntryCls().getCode(False))
- self.finish.emit()
- def __pasteCodeSym(self):
- if not self.__pasteCodeWarning():
- return
- self.paste.emit(self.libEntryCls().getCode(True))
- self.addSymbol.emit(self.libEntryCls.symbolName,
- self.blockName,
- self.blockName,
- self.libEntryCls.description)
- self.finish.emit()
- def __pasteCall(self):
- self._pasteCallGeneric(self.blockName,
- self.libEntryCls._isFB,
- self.libEntryCls.interfaceFields)
- self.addLibrary.emit(self.libEntryCls().makeSelection())
- self.finish.emit()
- def __pasteCallSym(self):
- self._pasteCallGeneric('"%s"' % self.libEntryCls.symbolName,
- self.libEntryCls._isFB,
- self.libEntryCls.interfaceFields)
- self.addSymbol.emit(self.libEntryCls.symbolName,
- self.blockName,
- self.blockName,
- self.libEntryCls.description)
- self.addLibrary.emit(self.libEntryCls().makeSelection())
- self.finish.emit()
- def defaultPaste(self):
- self.__pasteCallSym()
- class LibraryDialog(QDialog):
- ITEM_SFC = QListWidgetItem.UserType + 0
- ITEM_SFB = QListWidgetItem.UserType + 1
- ITEM_LIB_BASE = QListWidgetItem.UserType + 100
- BLOCK_OFFSET = QListWidgetItem.UserType + 0xFFFF
- def __init__(self, project, parent=None):
- QDialog.__init__(self, parent)
- self.setLayout(QGridLayout())
- self.setWindowTitle("AWL/STL - Standard library")
- self.setWindowIcon(getIcon("stdlib"))
- self.project = project
- self.pasteText = None
- self.pasteSymbol = None
- self.pasteLibSel = None
- self.currentActionWidget = None
- self.__nr2lib = {}
- self.__nr2entry = {}
- self.libList = QListWidget(self)
- QListWidgetItem("System functions (SFC)", self.libList, self.ITEM_SFC)
- QListWidgetItem("System function blocks (SFB)", self.libList, self.ITEM_SFB)
- for i, libName in enumerate(("IEC",)):
- try:
- lib = AwlLib.getByName(libName)
- except AwlSimError as e:
- MessageBox.handleAwlSimError(self, "Library error", e)
- continue
- self.__nr2lib[self.ITEM_LIB_BASE + i] = lib
- QListWidgetItem(lib.description, self.libList,
- self.ITEM_LIB_BASE + i)
- self.layout().addWidget(self.libList, 0, 0, 3, 1)
- self.libElemList = QListWidget(self)
- self.libElemList.setFont(getDefaultFixedFont())
- self.layout().addWidget(self.libElemList, 0, 1, 3, 1)
- self.iconLabel = QLabel(self)
- self.iconLabel.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
- self.iconLabel.setPixmap(getIcon("stdlib").pixmap(QSize(64, 64)))
- self.layout().addWidget(self.iconLabel, 0, 2)
- self.sysAction = SysActionWidget(self)
- self.sysAction.hide()
- self.layout().addWidget(self.sysAction, 1, 2)
- self.libAction = LibActionWidget(self)
- self.libAction.hide()
- self.layout().addWidget(self.libAction, 2, 2)
- self.libList.currentItemChanged.connect(self.__libItemChanged)
- self.libElemList.currentItemChanged.connect(self.__libElemItemChanged)
- self.libElemList.itemDoubleClicked.connect(self.__libElemDoubleClicked)
- for actionWidget in (self.sysAction, self.libAction):
- actionWidget.paste.connect(self.__actionPaste)
- actionWidget.addSymbol.connect(self.__actionAddSym)
- actionWidget.addLibrary.connect(self.__actionAddLib)
- actionWidget.finish.connect(self.accept)
- self.libList.setCurrentRow(0)
- self.libList.setMinimumWidth(190)
- self.libElemList.setMinimumWidth(350)
- self.iconLabel.setMinimumWidth(400)
- self.sysAction.setMinimumWidth(self.iconLabel.minimumWidth())
- self.libAction.setMinimumWidth(self.iconLabel.minimumWidth())
- self.resize(self.size().width(), 360)
- def __addLibElemList(self, entries):
- # Figure out the padding.
- maxA = maxB = ""
- for number, textA, textB, textC in entries:
- if len(textA) > len(maxA):
- maxA = textA
- if len(textB) > len(maxB):
- maxB = textB
- # Pad the entries and add them to the widget.
- for number, textA, textB, textC in entries:
- textA += " " * (len(maxA) - len(textA))
- textB += " " * (len(maxB) - len(textB))
- QListWidgetItem("%s %s%s" % (textA, textB, textC),
- self.libElemList,
- number)
- def __addSystemBlockTable(self, prefix, table):
- entries = []
- for blockCls in sorted(dictValues(table), key=lambda c: c.name[0]):
- if blockCls.broken:
- continue
- number, name, desc = blockCls.name
- conf = self.project.getCpuConf()
- if number < 0 and not conf.extInsnsEn:
- continue
- absName = "%s %d" % (prefix, number)
- symName = '"%s"' % name
- if desc:
- desc = " (%s)" % desc
- else:
- desc = ""
- entries.append((number + self.BLOCK_OFFSET,
- absName, symName, desc))
- self.__addLibElemList(entries)
- def __addLibraryTable(self, lib):
- entries = []
- for i, libCls in enumerate(sorted(lib.entries(),
- key=lambda c: c.staticIndex)):
- if libCls.broken:
- continue
- absName = "%s %d" % ("FC" if libCls._isFC else "FB",\
- libCls.staticIndex)
- symName = '"%s"' % libCls.symbolName
- if libCls.description:
- desc = " (%s)" % libCls.description
- else:
- desc = ""
- entries.append((i + self.BLOCK_OFFSET,
- absName, symName, desc))
- self.__nr2entry[i + self.BLOCK_OFFSET] = libCls
- self.__addLibElemList(entries)
- def __hideAllActionWidgets(self):
- self.currentActionWidget = None
- self.sysAction.hide()
- self.libAction.hide()
- def __libItemChanged(self, item, prevItem):
- self.__hideAllActionWidgets()
- self.libElemList.clear()
- if item.type() == self.ITEM_SFC:
- self.__addSystemBlockTable("SFC", SFC_table)
- elif item.type() == self.ITEM_SFB:
- self.__addSystemBlockTable("SFB", SFB_table)
- elif item.type() >= self.ITEM_LIB_BASE:
- lib = self.__nr2lib[item.type()]
- self.__addLibraryTable(lib)
- else:
- assert(0)
- def __libElemItemChanged(self, item, prevItem):
- if not item:
- self.__hideAllActionWidgets()
- return
- libType = self.libList.currentItem().type()
- if libType in (self.ITEM_SFC, self.ITEM_SFB):
- blockNum = item.type() - self.BLOCK_OFFSET
- self.sysAction.show()
- self.currentActionWidget = self.sysAction
- if libType == self.ITEM_SFC:
- self.sysAction.updateData("SFC", SFC_table[blockNum])
- else:
- self.sysAction.updateData("SFB", SFB_table[blockNum])
- elif libType >= self.ITEM_LIB_BASE:
- entryCls = self.__nr2entry[item.type()]
- self.libAction.show()
- self.currentActionWidget = self.libAction
- self.libAction.updateData(entryCls)
- else:
- assert(0)
- def __libElemDoubleClicked(self, item):
- if self.currentActionWidget:
- self.currentActionWidget.defaultPaste()
- def __actionPaste(self, text):
- assert(self.pasteText is None)
- self.pasteText = text
- def __actionAddSym(self, symbolName, address, dataType, comment):
- assert(self.pasteSymbol is None)
- self.pasteSymbol = (symbolName, address, dataType, comment)
- def __actionAddLib(self, libSelection):
- assert(self.pasteLibSel is None)
- self.pasteLibSel = libSelection
|