Move python evaluator to create a python plugin containing any related python module
--- a/PythonSTC.py Wed Jul 29 10:49:31 2009 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,596 +0,0 @@
-#----------------------------------------------------------------------
-This Python editor class comes from the wxPython demo, a bit tweaked
-#----------------------------------------------------------------------
-if wx.Platform == '__WXMSW__':
- faces = { 'times': 'Times New Roman',
- 'mono' : 'Courier New',
- 'other': 'Comic Sans MS',
-elif wx.Platform == '__WXMAC__':
- faces = { 'times': 'Times New Roman',
- 'other': 'Comic Sans MS',
- faces = { 'times': 'Times',
- 'other': 'new century schoolbook',
-#----------------------------------------------------------------------
-class PythonSTC(stc.StyledTextCtrl):
- def __init__(self, parent, ID,
- pos=wx.DefaultPosition, size=wx.DefaultSize,
- stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
- self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
- self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
- self.SetLexer(stc.STC_LEX_PYTHON)
- self.SetKeyWords(0, " ".join(keyword.kwlist))
- self.SetProperty("fold", "1")
- self.SetProperty("tab.timmy.whinge.level", "1")
- self.SetViewWhiteSpace(False)
- #self.SetBufferedDraw(False)
- #self.SetEOLMode(stc.STC_EOL_CRLF)
- #self.SetUseAntiAliasing(True)
- self.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
- # Setup a margin to hold fold markers
- #self.SetFoldFlags(16) ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER?
- self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
- self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
- self.SetMarginSensitive(2, True)
- self.SetMarginWidth(2, 12)
- if self.fold_symbols == 0:
- # Arrow pointing right for contracted folders, arrow pointing down for expanded
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_ARROWDOWN, "black", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_ARROW, "black", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "black", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "black", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
- elif self.fold_symbols == 1:
- # Plus for contracted folders, minus for expanded
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_MINUS, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_PLUS, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
- elif self.fold_symbols == 2:
- # Like a flattened tree control using circular headers and curved joins
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_CIRCLEMINUS, "white", "#404040")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_CIRCLEPLUS, "white", "#404040")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#404040")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNERCURVE, "white", "#404040")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE, "white", "#404040")
- elif self.fold_symbols == 3:
- # Like a flattened tree control using square headers
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "#808080")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "#808080")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#808080")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "#808080")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080")
- self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
- self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
- self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
- # Make some styles, The lexer defines what each style is used for, we
- # just have to define what each style looks like. This set is adapted from
- # Scintilla sample property files.
- # Global default styles for all languages
- self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
- self.StyleClearAll() # Reset all to be like the default
- # Global default styles for all languages
- self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
- self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces)
- self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces)
- self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold")
- self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
- self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
- self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
- self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces)
- self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
- self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
- self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces)
- self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces)
- self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces)
- # Class name definition
- self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % faces)
- # Function or method name definition
- self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % faces)
- self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces)
- self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
- self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces)
- # End of line where string is not closed
- self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces)
- self.SetCaretForeground("BLUE")
- def OnKeyPressed(self, event):
- if self.CallTipActive():
-# key = event.GetKeyCode()
-# if key == 32 and event.ControlDown():
-# pos = self.GetCurrentPos()
-# self.CallTipSetBackground("yellow")
-# self.CallTipShow(pos, 'lots of of text: blah, blah, blah\n\n'
-# 'show some suff, maybe parameters..\n\n'
-# 'fubar(param1, param2)')
-# #for x in range(50000):
-# # lst.append('%05d' % x)
-# #self.AutoCompShow(0, st)
-# kw = keyword.kwlist[:]
-# kw.append("__init__?3")
-# kw.append("zzaaaaa?2")
-# kw.append("zzbaaaa?2")
-# kw.append("this_is_a_longer_value")
-# #kw.append("this_is_a_much_much_much_much_much_much_much_longer_value")
-# kw.sort() # Python sorts are case sensitive
-# self.AutoCompSetIgnoreCase(False) # so this needs to match
-# # Images are specified with a appended "?type"
-# for i in range(len(kw)):
-# if kw[i] in keyword.kwlist:
-# self.AutoCompShow(0, " ".join(kw))
- def OnUpdateUI(self, evt):
- # check for matching braces
- caretPos = self.GetCurrentPos()
- charBefore = self.GetCharAt(caretPos - 1)
- styleBefore = self.GetStyleAt(caretPos - 1)
- if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
- braceAtCaret = caretPos - 1
- charAfter = self.GetCharAt(caretPos)
- styleAfter = self.GetStyleAt(caretPos)
- if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
- braceAtCaret = caretPos
- braceOpposite = self.BraceMatch(braceAtCaret)
- if braceAtCaret != -1 and braceOpposite == -1:
- self.BraceBadLight(braceAtCaret)
- self.BraceHighlight(braceAtCaret, braceOpposite)
- #pt = self.PointFromPosition(braceOpposite)
- #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
- def OnMarginClick(self, evt):
- # fold and unfold as needed
- if evt.GetMargin() == 2:
- if evt.GetShift() and evt.GetControl():
- lineClicked = self.LineFromPosition(evt.GetPosition())
- if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
- self.SetFoldExpanded(lineClicked, True)
- self.Expand(lineClicked, True, True, 1)
- if self.GetFoldExpanded(lineClicked):
- self.SetFoldExpanded(lineClicked, False)
- self.Expand(lineClicked, False, True, 0)
- self.SetFoldExpanded(lineClicked, True)
- self.Expand(lineClicked, True, True, 100)
- self.ToggleFold(lineClicked)
- lineCount = self.GetLineCount()
- # find out if we are folding or unfolding
- for lineNum in range(lineCount):
- if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
- expanding = not self.GetFoldExpanded(lineNum)
- while lineNum < lineCount:
- level = self.GetFoldLevel(lineNum)
- if level & stc.STC_FOLDLEVELHEADERFLAG and \
- (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
- self.SetFoldExpanded(lineNum, True)
- lineNum = self.Expand(lineNum, True)
- lastChild = self.GetLastChild(lineNum, -1)
- self.SetFoldExpanded(lineNum, False)
- if lastChild > lineNum:
- self.HideLines(lineNum+1, lastChild)
- def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
- lastChild = self.GetLastChild(line, level)
- while line <= lastChild:
- self.ShowLines(line, line)
- self.HideLines(line, line)
- self.ShowLines(line, line)
- level = self.GetFoldLevel(line)
- if level & stc.STC_FOLDLEVELHEADERFLAG:
- self.SetFoldExpanded(line, True)
- self.SetFoldExpanded(line, False)
- line = self.Expand(line, doExpand, force, visLevels-1)
- if doExpand and self.GetFoldExpanded(line):
- line = self.Expand(line, True, force, visLevels-1)
- line = self.Expand(line, False, force, visLevels-1)
-#----------------------------------------------------------------------
-class PythonCodeEditor(PythonSTC):
- def __init__(self, parent):
- PythonSTC.__init__(self, parent, -1, style=wx.BORDER_NONE)
- # Some methods to make it compatible with how the wxTextCtrl is used
- def SetValue(self, value):
- value = value.decode('utf-8')
- return self.GetModify()
- def SetInsertionPoint(self, pos):
- self.SetCurrentPos(pos)
- def ShowPosition(self, pos):
- line = self.LineFromPosition(pos)
- #self.EnsureVisible(line)
- def GetLastPosition(self):
- return self.GetLength()
- def GetPositionFromLine(self, line):
- return self.PositionFromLine(line)
- def GetRange(self, start, end):
- return self.GetTextRange(start, end)
- def GetSelection(self):
- return self.GetAnchor(), self.GetCurrentPos()
- def SetSelection(self, start, end):
- self.SetSelectionStart(start)
- self.SetSelectionEnd(end)
- def SelectLine(self, line):
- start = self.PositionFromLine(line)
- end = self.GetLineEndPosition(line)
- self.SetSelection(start, end)
- This method carries out the work of setting up the demo editor.
- It's seperate so as not to clutter up the init code.
- self.SetLexer(stc.STC_LEX_PYTHON)
- self.SetKeyWords(0, " ".join(keyword.kwlist))
- self.SetProperty("fold", "1" )
- # Highlight tab/space mixing (shouldn't be any)
- self.SetProperty("tab.timmy.whinge.level", "1")
- # Set left and right margins
- # Set up the numbers in the margin for margin #1
- self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
- # Reasonable value for, say, 4-5 digits using a mono font (40 pix)
- self.SetMarginWidth(1, 40)
- # Indentation and tab stuff
- self.SetIndent(4) # Proscribed indent size for wx
- self.SetIndentationGuides(True) # Show indent guides
- self.SetBackSpaceUnIndents(True)# Backspace unindents rather than delete 1 space
- self.SetTabIndents(True) # Tab key indents
- self.SetTabWidth(4) # Proscribed tab size for wx
- self.SetUseTabs(False) # Use spaces rather than tabs, or
- # TabTimmy will complain!
- self.SetViewWhiteSpace(False) # Don't view white space
- # EOL: Since we are loading/saving ourselves, and the
- # strings will always have \n's in them, set the STC to
- self.SetEOLMode(wx.stc.STC_EOL_LF)
- # No right-edge mode indicator
- self.SetEdgeMode(stc.STC_EDGE_NONE)
- # Setup a margin to hold fold markers
- self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
- self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
- self.SetMarginSensitive(2, True)
- self.SetMarginWidth(2, 12)
- # and now set up the fold markers
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "black")
- self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "black")
- if wx.Platform == '__WXMSW__':
- self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
- 'fore:#000000,back:#FFFFFF,face:Courier New')
- elif wx.Platform == '__WXMAC__':
- # TODO: if this looks fine on Linux too, remove the Mac-specific case
- # and use this whenever OS != MSW.
- self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
- 'fore:#000000,back:#FFFFFF,face:Monaco')
- defsize = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT).GetPointSize()
- self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
- 'fore:#000000,back:#FFFFFF,face:Courier,size:%d'%defsize)
- # Clear styles and revert to default.
- # Following style specs only indicate differences from default.
- # The rest remains unchanged.
- # Line numbers in margin
- self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2')
- self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00')
- self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000')
- self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD")
- self.StyleSetSpec(wx.stc.STC_P_DEFAULT, 'fore:#000000')
- self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, 'fore:#008000,back:#F0FFF0')
- self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0')
- self.StyleSetSpec(wx.stc.STC_P_NUMBER, 'fore:#008080')
- # Strings and characters
- self.StyleSetSpec(wx.stc.STC_P_STRING, 'fore:#800080')
- self.StyleSetSpec(wx.stc.STC_P_CHARACTER, 'fore:#800080')
- self.StyleSetSpec(wx.stc.STC_P_WORD, 'fore:#000080,bold')
- self.StyleSetSpec(wx.stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA')
- self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA')
- self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, 'fore:#0000FF,bold')
- self.StyleSetSpec(wx.stc.STC_P_DEFNAME, 'fore:#008080,bold')
- self.StyleSetSpec(wx.stc.STC_P_OPERATOR, 'fore:#800000,bold')
- # Identifiers. I leave this as not bold because everything seems
- # to be an identifier if it doesn't match the above criterae
- self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, 'fore:#000000')
- self.SetCaretForeground("BLUE")
- self.SetSelBackground(1, '#66CCFF')
- self.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT))
- self.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))
- def RegisterModifiedEvent(self, eventHandler):
- self.Bind(wx.stc.EVT_STC_CHANGE, eventHandler)
-class PythonCodePanel(wx.Panel):
- """Panel for the 'Demo Code' tab"""
- def __init__(self, parent, mainFrame):
- wx.Panel.__init__(self, parent, size=(1,1))
- self.mainFrame = mainFrame
- self.editor = PythonCodeEditor(self)
- self.editor.RegisterModifiedEvent(self.OnCodeModified)
- self.btnSave = wx.Button(self, -1, "Save")
- self.btnRestore = wx.Button(self, -1, "Restore")
- self.btnSave.Enable(False)
- self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
- self.btnRestore.Bind(wx.EVT_BUTTON, self.OnRestore)
- self.controlBox = wx.BoxSizer(wx.HORIZONTAL)
- self.controlBox.Add(self.btnSave, 0, wx.RIGHT, 5)
- self.controlBox.Add(self.btnRestore, 0)
- self.box = wx.BoxSizer(wx.VERTICAL)
- self.box.Add(self.controlBox, 0, wx.EXPAND)
- self.box.Add(wx.StaticLine(self), 0, wx.EXPAND)
- self.box.Add(self.editor, 1, wx.EXPAND)
- self.SetSizer(self.box)
- self.Bind(wx.EVT_MENU, self.OnSave, id=wx.ID_SAVE)
- accel = wx.AcceleratorTable([wx.AcceleratorEntry(wx.ACCEL_CTRL, 83, wx.ID_SAVE)])
- self.SetAcceleratorTable(accel)
- # Loads from a file object
- def LoadSourceFile(self, filename):
- self.sourceFile = filename
- if os.path.exists(filename):
- self.LoadSource(file(filename).read())
- def LoadSource(self, source):
- self.editor.SetValue(source)
- self.btnSave.Enable(False)
- def JumpToLine(self, line, highlight=False):
- self.editor.GotoLine(line)
- self.editor.SelectLine(line)
- def OnCodeModified(self, event):
- self.btnSave.Enable(self.editor.IsModified())
- def OnSave(self, event):
- overwriteMsg = "You are about to overwrite that file\n" + \
- "Do you want to continue?"
- dlg = wx.MessageDialog(self, overwriteMsg, "wxPython Demo",
- wx.YES_NO | wx.NO_DEFAULT| wx.ICON_EXCLAMATION)
- result = dlg.ShowModal()
- source = self.editor.GetText().encode("utf-8")
- f = file(self.sourceFile, "w")
- #self.mainFrame.SetTreeModified(True)
- def OnRestore(self, event):
- self.LoadSourceFile(self.sourceFile)
- self.btnSave.Enable(self.editor.IsModified())
--- a/plugger.py Wed Jul 29 10:49:31 2009 +0200
+++ b/plugger.py Wed Jul 29 15:17:10 2009 +0200
@@ -910,14 +910,6 @@
# define name for IEC raw code file
return os.path.join(self.PlugPath(), "raw_plc.st")
- def _getPYTHONcodepath(self):
- # define name for IEC raw code file
- return os.path.join(self.PlugPath(), "runtime.py")
- def _getWXGLADEpath(self):
- # define name for IEC raw code file
- return os.path.join(self.PlugPath(), "hmi.wxg")
filepath = os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h")
@@ -1073,15 +1065,6 @@
- def launch_wxglade(self, options, wait=False):
- from wxglade import __file__ as fileName
- path = os.path.dirname(fileName)
- glade = os.path.join(path, 'wxglade.py')
- if wx.Platform == '__WXMSW__':
- mode = {False:os.P_NOWAIT, True:os.P_WAIT}[wait]
- os.spawnv(mode, sys.executable, ["\"%s\""%sys.executable] + [glade] + options)
#######################################################################
# C CODE GENERATION METHODS
@@ -1096,27 +1079,10 @@
@return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
- res = ([(C_file_name, self.plcCFLAGS)
+ return ([(C_file_name, self.plcCFLAGS) for C_file_name in self.PLCGeneratedCFiles ],
False) # do not expose retreive/publish calls
- pyfile=self._getPYTHONcodepath()
- if os.path.exists(pyfile):
- res += (("runtime.py", file(pyfile,"rb")),)
- wxgfile=self._getWXGLADEpath()
- if os.path.exists(wxgfile):
- hmipyfile=os.path.join(self._getBuildPath(),"hmi.py")
- if wx.Platform == '__WXMSW__':
- wxgfile = "\"%s\""%wxgfile
- _hmipyfile = "\"%s\""%hmipyfile
- self.launch_wxglade(['-o', _hmipyfile, '-g', 'python', wxgfile], wait=True)
- res += (("hmi.py", file(hmipyfile,"rb")),)
def ResetIECProgramsAndVariables(self):
@@ -1211,23 +1177,6 @@
- def Generate_plc_python(self):
- Generate trace/debug code out of PLC variable list
- self.GetIECProgramsAndVariables()
- python_eval_fb_list = []
- for v in self._VariablesList :
- if v["vartype"] == "FB" and v["type"] in ["PYTHON_EVAL","PYTHON_POLL"]:
- python_eval_fb_list.append(v)
- python_eval_fb_count = max(1, len(python_eval_fb_list))
- python_code = targets.code("plc_python") % {
- "python_eval_fb_count": python_eval_fb_count}
def Generate_plc_common_main(self):
Use plugins layout given in LocationCFilesAndCFLAGS to
@@ -1334,8 +1283,6 @@
for generator, filename, name in [
(self.Generate_plc_debugger, "plc_debugger.c", "Debugger"),
- # IEC<->python gateway code
- (self.Generate_plc_python, "plc_python.c", "IEC-Python gateway"),
# init/cleanup/retrieve/publish, run and align code
(self.Generate_plc_common_main,"plc_common_main.c","Common runtime")]:
@@ -1407,32 +1354,6 @@
- def _editPYTHONcode(self):
- from PythonSTC import PythonCodePanel
- new_dialog = wx.Frame(self.AppFrame)
- PYTHON_viewer = PythonCodePanel(new_dialog, self.AppFrame)
- #ST_viewer.Enable(False)
- pyfile=self._getPYTHONcodepath()
- PYTHON_viewer.LoadSourceFile(pyfile)
- def _editWXGLADE(self):
- wxg_filename = self._getWXGLADEpath()
- if not os.path.exists(wxg_filename):
- open(wxg_filename,"w").write("""<?xml version="1.0"?>
-<application path="" name="" class="" option="0" language="python" top_window="frame_1" encoding="UTF-8" use_gettext="0" overwrite="0" use_new_namespace="1" for_version="2.8" is_template="0">
- <object class="HMIFrame" name="frame_1" base="EditFrame">
- <style>wxDEFAULT_FRAME_STYLE</style>
- if wx.Platform == '__WXMSW__':
- wxg_filename = "\"%s\""%wxg_filename
- self.launch_wxglade([wxg_filename])
if self.PLCEditor is None:
self.RefreshPluginsBlockLists()
@@ -1873,12 +1794,4 @@
"name" : _("Raw IEC code"),
"tooltip" : _("Edit raw IEC code added to code generated by PLCGenerator"),
"method" : "_editIECrawcode"},
- {"bitmap" : opjimg("editPYTHONcode"),
- "name" : "Python code",
- "tooltip" : "Write Python runtime code, for use with python_eval FBs",
- "method" : "_editPYTHONcode"},
- {"bitmap" : opjimg("editWXGLADE"),
- "name" : "WXGLADE GUI",
- "tooltip" : "Edit a WxWidgets GUI with WXGlade",
- "method" : "_editWXGLADE"},
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/python/PythonEditor.py Wed Jul 29 15:17:10 2009 +0200
@@ -0,0 +1,594 @@
+if wx.Platform == '__WXMSW__': + faces = { 'times': 'Times New Roman', + 'mono' : 'Courier New', + 'other': 'Comic Sans MS', +elif wx.Platform == '__WXMAC__': + faces = { 'times': 'Times New Roman', + 'other': 'Comic Sans MS', + faces = { 'times': 'Times', + 'other': 'new century schoolbook', +def AppendMenu(parent, help, id, kind, text): + if wx.VERSION >= (2, 6, 0): + parent.Append(help=help, id=id, kind=kind, text=text) + parent.Append(helpString=help, id=id, kind=kind, item=text) +] = [wx.NewId() for _init_ctrls in range(1)] +def GetCursorPos(old, new): + common_length = min(old_length, new_length) + for i in xrange(common_length): + if old_length < new_length: + if common_length > 0 and old[i] != new[i]: + return i + new_length - old_length + return i + new_length - old_length + 1 + elif old_length > new_length or i < min(old_length, new_length) - 1: + if common_length > 0 and old[i] != new[i]: +class PythonEditor(stc.StyledTextCtrl): + def __init__(self, parent, window, controler): + stc.StyledTextCtrl.__init__(self, parent, ID_PYTHONEDITOR, wx.DefaultPosition, + self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN) + self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT) + self.SetLexer(stc.STC_LEX_PYTHON) + self.SetKeyWords(0, " ".join(keyword.kwlist)) + self.SetProperty("fold", "1") + self.SetProperty("tab.timmy.whinge.level", "1") + self.SetViewWhiteSpace(False) + #self.SetBufferedDraw(False) + #self.SetEOLMode(stc.STC_EOL_CRLF) + #self.SetUseAntiAliasing(True) + self.SetEdgeMode(stc.STC_EDGE_BACKGROUND) + # Set up the numbers in the margin for margin #1 + self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER) + # Reasonable value for, say, 4-5 digits using a mono font (40 pix) + self.SetMarginWidth(1, 40) + # Setup a margin to hold fold markers + self.SetMarginType(2, stc.STC_MARGIN_SYMBOL) + self.SetMarginMask(2, stc.STC_MASK_FOLDERS) + self.SetMarginSensitive(2, True) + self.SetMarginWidth(2, 12) + if self.fold_symbols == 0: + # Arrow pointing right for contracted folders, arrow pointing down for expanded + self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_ARROWDOWN, "black", "black") + self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_ARROW, "black", "black") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "black", "black") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "black", "black") + self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black") + self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black") + elif self.fold_symbols == 1: + # Plus for contracted folders, minus for expanded + self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_MINUS, "white", "black") + self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_PLUS, "white", "black") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "white", "black") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "white", "black") + self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black") + self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black") + elif self.fold_symbols == 2: + # Like a flattened tree control using circular headers and curved joins + self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_CIRCLEMINUS, "white", "#404040") + self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_CIRCLEPLUS, "white", "#404040") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#404040") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNERCURVE, "white", "#404040") + self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040") + self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE, "white", "#404040") + elif self.fold_symbols == 3: + # Like a flattened tree control using square headers + self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "#808080") + self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "#808080") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#808080") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "#808080") + self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080") + self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080") + self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080") + self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI) + self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick) + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed) + if wx.Platform == '__WXMSW__': + self.StyleSetSpec(stc.STC_STYLE_DEFAULT, + 'fore:#000000,back:#FFFFFF,face:Courier New') + elif wx.Platform == '__WXMAC__': + # TODO: if this looks fine on Linux too, remove the Mac-specific case + # and use this whenever OS != MSW. + self.StyleSetSpec(stc.STC_STYLE_DEFAULT, + 'fore:#000000,back:#FFFFFF,face:Monaco') + defsize = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT).GetPointSize() + self.StyleSetSpec(stc.STC_STYLE_DEFAULT, + 'fore:#000000,back:#FFFFFF,face:Courier,size:%d'%defsize) + # Clear styles and revert to default. + # Following style specs only indicate differences from default. + # The rest remains unchanged. + # Line numbers in margin + self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2') + self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00') + self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000') + self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD") + self.StyleSetSpec(wx.stc.STC_P_DEFAULT, 'fore:#000000') + self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, 'fore:#008000,back:#F0FFF0') + self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0') + self.StyleSetSpec(wx.stc.STC_P_NUMBER, 'fore:#008080') + # Strings and characters + self.StyleSetSpec(wx.stc.STC_P_STRING, 'fore:#800080') + self.StyleSetSpec(wx.stc.STC_P_CHARACTER, 'fore:#800080') + self.StyleSetSpec(wx.stc.STC_P_WORD, 'fore:#000080,bold') + self.StyleSetSpec(wx.stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA') + self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA') + self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, 'fore:#0000FF,bold') + self.StyleSetSpec(wx.stc.STC_P_DEFNAME, 'fore:#008080,bold') + self.StyleSetSpec(wx.stc.STC_P_OPERATOR, 'fore:#800000,bold') + # Identifiers. I leave this as not bold because everything seems + # to be an identifier if it doesn't match the above criterae + self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, 'fore:#000000') + self.SetCaretForeground("BLUE") + self.SetSelBackground(1, '#66CCFF') + self.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)) + self.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)) + # register some images for use in the AutoComplete box. + #self.RegisterImage(1, images.getSmilesBitmap()) + wx.ArtProvider.GetBitmap(wx.ART_DELETE, size=(16,16))) + wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16))) + wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16))) + # Indentation and tab stuff + self.SetIndent(4) # Proscribed indent size for wx + self.SetIndentationGuides(True) # Show indent guides + self.SetBackSpaceUnIndents(True)# Backspace unindents rather than delete 1 space + self.SetTabIndents(True) # Tab key indents + self.SetTabWidth(4) # Proscribed tab size for wx + self.SetUseTabs(False) # Use spaces rather than tabs, or + # TabTimmy will complain! + self.SetViewWhiteSpace(False) # Don't view white space + # EOL: Since we are loading/saving ourselves, and the + # strings will always have \n's in them, set the STC to + self.SetEOLMode(wx.stc.STC_EOL_LF) + # No right-edge mode indicator + self.SetEdgeMode(stc.STC_EDGE_NONE) + self.Controler = controler + self.ParentWindow = window + self.DisableEvents = True + self.CurrentAction = None + self.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT|wx.stc.STC_MOD_BEFOREDELETE) + self.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop, id=ID_PYTHONEDITOR) + self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) + self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification, id=ID_PYTHONEDITOR) + def OnModification(self, event): + if not self.DisableEvents: + mod_type = event.GetModificationType() + if not (mod_type&wx.stc.STC_PERFORMED_UNDO or mod_type&wx.stc.STC_PERFORMED_REDO): + if mod_type&wx.stc.STC_MOD_BEFOREINSERT: + if self.CurrentAction == None: + elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1: + self.Controler.EndBuffering() + self.CurrentAction = ("Add", event.GetPosition()) + elif mod_type&wx.stc.STC_MOD_BEFOREDELETE: + if self.CurrentAction == None: + elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1: + self.Controler.EndBuffering() + self.CurrentAction = ("Delete", event.GetPosition()) + def OnDoDrop(self, event): + wx.CallAfter(self.RefreshModel) + def IsViewing(self, name): + return self.Name == name + # Buffer the last model state + def RefreshBuffer(self): + self.Controler.BufferPython() + self.ParentWindow.RefreshTitle() + self.ParentWindow.RefreshEditMenu() + def StartBuffering(self): + self.Controler.StartBuffering() + self.ParentWindow.RefreshTitle() + self.ParentWindow.RefreshEditMenu() + if self.CurrentAction != None: + self.Controler.EndBuffering() + self.CurrentAction = None + self.DisableEvents = True + old_cursor_pos = self.GetCurrentPos() + old_text = self.GetText() + new_text = self.Controler.GetPythonCode() + new_cursor_pos = GetCursorPos(old_text, new_text) + if new_cursor_pos != None: + self.GotoPos(new_cursor_pos) + self.GotoPos(old_cursor_pos) + self.DisableEvents = False + def RefreshModel(self): + self.Controler.SetPythonCode(self.GetText()) + def OnKeyPressed(self, event): + if self.CallTipActive(): + key = event.GetKeyCode() + if key == 32 and event.ControlDown(): + pos = self.GetCurrentPos() +## self.CallTipSetBackground("yellow") +## self.CallTipShow(pos, 'lots of of text: blah, blah, blah\n\n' +## 'show some suff, maybe parameters..\n\n' +## 'fubar(param1, param2)') + self.AutoCompSetIgnoreCase(False) # so this needs to match + # Images are specified with a appended "?type" + self.AutoCompShow(0, " ".join([word + "?1" for word in keyword.kwlist])) + wx.CallAfter(self.RefreshModel) + def OnKillFocus(self, event): + def OnUpdateUI(self, evt): + # check for matching braces + caretPos = self.GetCurrentPos() + charBefore = self.GetCharAt(caretPos - 1) + styleBefore = self.GetStyleAt(caretPos - 1) + if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR: + braceAtCaret = caretPos - 1 + charAfter = self.GetCharAt(caretPos) + styleAfter = self.GetStyleAt(caretPos) + if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR: + braceAtCaret = caretPos + braceOpposite = self.BraceMatch(braceAtCaret) + if braceAtCaret != -1 and braceOpposite == -1: + self.BraceBadLight(braceAtCaret) + self.BraceHighlight(braceAtCaret, braceOpposite) + #pt = self.PointFromPosition(braceOpposite) + #self.Refresh(True, wxRect(pt.x, pt.y, 5,5)) + def OnMarginClick(self, evt): + # fold and unfold as needed + if evt.GetMargin() == 2: + if evt.GetShift() and evt.GetControl(): + lineClicked = self.LineFromPosition(evt.GetPosition()) + if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG: + self.SetFoldExpanded(lineClicked, True) + self.Expand(lineClicked, True, True, 1) + if self.GetFoldExpanded(lineClicked): + self.SetFoldExpanded(lineClicked, False) + self.Expand(lineClicked, False, True, 0) + self.SetFoldExpanded(lineClicked, True) + self.Expand(lineClicked, True, True, 100) + self.ToggleFold(lineClicked) + lineCount = self.GetLineCount() + # find out if we are folding or unfolding + for lineNum in range(lineCount): + if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG: + expanding = not self.GetFoldExpanded(lineNum) + while lineNum < lineCount: + level = self.GetFoldLevel(lineNum) + if level & stc.STC_FOLDLEVELHEADERFLAG and \ + (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE: + self.SetFoldExpanded(lineNum, True) + lineNum = self.Expand(lineNum, True) + lastChild = self.GetLastChild(lineNum, -1) + self.SetFoldExpanded(lineNum, False) + if lastChild > lineNum: + self.HideLines(lineNum+1, lastChild) + def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): + lastChild = self.GetLastChild(line, level) + while line <= lastChild: + self.ShowLines(line, line) + self.HideLines(line, line) + self.ShowLines(line, line) + level = self.GetFoldLevel(line) + if level & stc.STC_FOLDLEVELHEADERFLAG: + self.SetFoldExpanded(line, True) + self.SetFoldExpanded(line, False) + line = self.Expand(line, doExpand, force, visLevels-1) + if doExpand and self.GetFoldExpanded(line): + line = self.Expand(line, True, force, visLevels-1) + line = self.Expand(line, False, force, visLevels-1) +#------------------------------------------------------------------------------- +# PythonEditor Main Frame Class +#------------------------------------------------------------------------------- +] = [wx.NewId() for _init_ctrls in range(1)] +class PythonEditorFrame(wx.Frame): + if wx.VERSION < (2, 6, 0): + def Bind(self, event, function, id = None): + event(self, id, function) + def _init_coll_EditMenu_Items(self, parent): + AppendMenu(parent, help='', id=wx.ID_REFRESH, + kind=wx.ITEM_NORMAL, text=u'Refresh\tCTRL+R') + AppendMenu(parent, help='', id=wx.ID_UNDO, + kind=wx.ITEM_NORMAL, text=u'Undo\tCTRL+Z') + AppendMenu(parent, help='', id=wx.ID_REDO, + kind=wx.ITEM_NORMAL, text=u'Redo\tCTRL+Y') + self.Bind(wx.EVT_MENU, self.OnRefreshMenu, id=wx.ID_REFRESH) + self.Bind(wx.EVT_MENU, self.OnUndoMenu, id=wx.ID_UNDO) + self.Bind(wx.EVT_MENU, self.OnRedoMenu, id=wx.ID_REDO) + def _init_coll_MenuBar_Menus(self, parent): + parent.Append(menu=self.EditMenu, title=u'&Edit') + self.MenuBar = wx.MenuBar() + self.EditMenu = wx.Menu(title='') + self._init_coll_MenuBar_Menus(self.MenuBar) + self._init_coll_EditMenu_Items(self.EditMenu) + def _init_ctrls(self, prnt): + wx.Frame.__init__(self, id=ID_PYTHONEDITORFRAME, name=u'PythonEditor', + parent=prnt, pos=wx.DefaultPosition, size=wx.Size(800, 650), + style=wx.DEFAULT_FRAME_STYLE, title=u'PythonEditor') + self.SetClientSize(wx.Size(1000, 600)) + self.SetMenuBar(self.MenuBar) + self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) + self.Bind(wx.EVT_MENU, self.OnSaveMenu, id=wx.ID_SAVE) + accel = wx.AcceleratorTable([wx.AcceleratorEntry(wx.ACCEL_CTRL, 83, wx.ID_SAVE)]) + self.SetAcceleratorTable(accel) + if wx.VERSION >= (2, 8, 0): + self.AUIManager = wx.aui.AuiManager(self) + self.AUIManager.SetDockSizeConstraint(0.5, 0.5) + self.PythonEdited = PythonEditor(self, self, self.Controler) + if wx.VERSION < (2, 8, 0): + self.MainSizer = wx.BoxSizer(wx.VERTICAL) + self.MainSizer.AddWindow(self.PythonEdited, 0, border=0, flag=wx.GROW) + self.SetSizer(self.MainSizer) + self.AUIManager.AddPane(self.PythonEdited, wx.aui.AuiPaneInfo().CentrePane()) + self.StatusBar = wx.StatusBar( name='HelpBar', + parent=self, style=wx.ST_SIZEGRIP) + self.SetStatusBar(self.StatusBar) + if wx.VERSION >= (2, 8, 0): + self.AUIManager.Update() + def __init__(self, parent, controler): + self.Controler = controler + self._init_ctrls(parent) + self.PythonEdited.RefreshView() + def OnCloseFrame(self, event): + if wx.VERSION >= (2, 8, 0): + self.AUIManager.UnInit() + if getattr(self, "_onclose", None) is not None: + def OnSaveMenu(self, event): + if getattr(self, "_onsave", None) != None: + def RefreshTitle(self): + self.SetTitle("PythonEditor - %s"%self.Controler.GetFilename()) +#------------------------------------------------------------------------------- +# Edit Project Menu Functions +#------------------------------------------------------------------------------- + def RefreshEditMenu(self): + undo, redo = self.Controler.GetBufferState() + self.EditMenu.Enable(wx.ID_UNDO, undo) + self.EditMenu.Enable(wx.ID_REDO, redo) + def OnRefreshMenu(self, event): + self.PythonEdited.RefreshView() + def OnUndoMenu(self, event): + self.Controler.LoadPrevious() + self.PythonEdited.RefreshView() + def OnRedoMenu(self, event): + self.Controler.LoadNext() + self.PythonEdited.RefreshView() --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/python/README Wed Jul 29 15:17:10 2009 +0200
@@ -0,0 +1,1 @@
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/python/__init__.py Wed Jul 29 15:17:10 2009 +0200
@@ -0,0 +1,1 @@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/python/modules/__init__.py Wed Jul 29 15:17:10 2009 +0200
@@ -0,0 +1,13 @@
+from os import listdir, path +_base_path = path.split(__file__)[0] +__all__ = [name for name in listdir(_base_path) if path.isdir(path.join(_base_path, name)) and name.upper() != "CVS" or name.endswith(".py") and not name.startswith("__")] + helpfilename = path.join(_base_path, name, "README") + if path.isfile(helpfilename): + helps.append(open(helpfilename).readline().strip()) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/python/plc_python.c Wed Jul 29 15:17:10 2009 +0200
@@ -0,0 +1,214 @@
+ * Python Asynchronous execution code + * PLC put python commands in a fifo, respecting execution order + * with the help of C pragmas inserted in python_eval FB code + * Buffer content is read asynchronously, (from non real time part), + * commands are executed and result stored for later use by PLC. + * In this implementation, fifo is a list of pointer to python_eval + * function blocks structures. Some local variables have been added in + * python_eval interface. We use those local variables as buffer and state +#include "iec_types_all.h" +/* The fifo (fixed size, as number of FB is fixed) */ +static PYTHON_EVAL* EvalFBs[%(python_eval_fb_count)d]; +/* Producer and consumer cursors */ +static int Current_PLC_EvalFB; +static int Current_Python_EvalFB; +/* A global IEC-Python gateway state, for use inside python_eval FBs*/ +#define PYTHON_LOCKED_BY_PYTHON 0 +#define PYTHON_LOCKED_BY_PLC 1 +#define PYTHON_MUSTWAKEUP 2 +#define PYTHON_FINISHED 4 +/* Each python_eval FunctionBlock have it own state */ +#define PYTHON_FB_FREE 0 +#define PYTHON_FB_REQUESTED 1 +#define PYTHON_FB_PROCESSING 2 +#define PYTHON_FB_ANSWERED 3 +int WaitPythonCommands(void); +void UnBlockPythonCommands(void); +int TryLockPython(void); +void UnLockPython(void); +int __init_%(location)s() + /* Initialize cursors */ + Current_Python_EvalFB = 0; + Current_PLC_EvalFB = 0; + PythonState = PYTHON_LOCKED_BY_PYTHON; + for(i = 0; i < %(python_eval_fb_count)d; i++) +void __cleanup_%(location)s() + PythonState = PYTHON_FINISHED; + UnBlockPythonCommands(); +void __retrieve_%(location)s() + /* Check Python thread is not being + * modifying internal python_eval data */ + PythonState = TryLockPython() ? + PYTHON_LOCKED_BY_PYTHON; + /* If python thread _is_ in, then PythonState remains PYTHON_LOCKED_BY_PYTHON + * and python_eval will no do anything */ +void __publish_%(location)s() + if(PythonState & PYTHON_LOCKED_BY_PLC){ + /* If runnig PLC did push something in the fifo*/ + if(PythonState & PYTHON_MUSTWAKEUP){ + /* WakeUp python thread */ + UnBlockPythonCommands(); + * Called by the PLC, each time a python_eval + * FB instance is executed +void __PythonEvalFB(int poll, PYTHON_EVAL* data__) + /* detect rising edge on TRIG to trigger evaluation */ + if(((data__->TRIG && !data__->TRIGM1) || + /* polling is equivalent to trig on value rather than on rising edge*/ + (poll && data__->TRIG )) && + /* trig only if not already trigged */ + /* make a safe copy of the code */ + data__->PREBUFFER = data__->CODE; + /* retain value for next rising edge detection */ + data__->TRIGM1 = data__->TRIG; + /* python thread is not in ? */ + if( PythonState & PYTHON_LOCKED_BY_PLC){ + /* if some answer are waiting, publish*/ + if(data__->STATE == PYTHON_FB_ANSWERED){ + /* Copy buffer content into result*/ + data__->RESULT = data__->BUFFER; + /* signal result presece to PLC*/ + data__->STATE = PYTHON_FB_FREE; + /* mark as not trigged */ + /*printf("__PythonEvalFB pop %%d - %%*s\n",Current_PLC_EvalFB, data__->BUFFER.len, data__->BUFFER.body);*/ + /* when in polling, no answer == ack down */ + /* got the order to act ?*/ + if(data__->TRIGGED == 1 && + /* and not already being processed */ + data__->STATE == PYTHON_FB_FREE) + /* Enter the block in the fifo + /* Don't have to check if fifo cell is free + * as fifo size == FB count, and a FB cannot + * be requested twice */ + EvalFBs[Current_PLC_EvalFB] = data__; + /* copy into BUFFER local*/ + data__->BUFFER = data__->PREBUFFER; + /* Set ACK pin to low so that we can set a rising edge on result */ + /* when not polling, a new answer imply reseting ack*/ + /* when in polling, acting reset trigger */ + data__->STATE = PYTHON_FB_REQUESTED; + /* Have to wakeup python thread in case he was asleep */ + PythonState |= PYTHON_MUSTWAKEUP; + /*printf("__PythonEvalFB push %%d - %%*s\n",Current_PLC_EvalFB, data__->BUFFER.len, data__->BUFFER.body);*/ + Current_PLC_EvalFB = (Current_PLC_EvalFB + 1) %% %(python_eval_fb_count)d; +char* PythonIterator(char* result) + //printf("PythonIterator result %%s\n", result); + /* take python mutex to prevent changing PLC data while PLC running */ + data__ = EvalFBs[Current_Python_EvalFB]; + if(data__ && /* may be null at first run */ + data__->STATE == PYTHON_FB_PROCESSING){ /* some answer awaited*/ + /* If result not None */ + data__->BUFFER.len = strlen(result); + /* prevent results overrun */ + if(data__->BUFFER.len > STR_MAX_LEN) + data__->BUFFER.len = STR_MAX_LEN; + /* TODO : signal error */ + /* Copy results to buffer */ + strncpy(data__->BUFFER.body, result, data__->BUFFER.len); + data__->BUFFER.len = 0; + /* remove block from fifo*/ + EvalFBs[Current_Python_EvalFB] = NULL; + /* Mark block as answered */ + data__->STATE = PYTHON_FB_ANSWERED; + Current_Python_EvalFB = (Current_Python_EvalFB + 1) %% %(python_eval_fb_count)d; + //printf("PythonIterator ++ Current_Python_EvalFB %%d\n", Current_Python_EvalFB); + /* while next slot is empty */ + while(((data__ = EvalFBs[Current_Python_EvalFB]) == NULL) || + /* or doesn't contain command */ + data__->STATE != PYTHON_FB_REQUESTED) + /* wait next FB to eval */ + //printf("PythonIterator wait\n"); + if(WaitPythonCommands()) return NULL; + if(PythonState & PYTHON_FINISHED) return NULL; + /* Mark block as processing */ + data__->STATE = PYTHON_FB_PROCESSING; + //printf("PythonIterator\n"); + /* make BUFFER a null terminated string */ + data__->BUFFER.body[data__->BUFFER.len] = 0; + /* next command is BUFFER */ + next_command = data__->BUFFER.body; + /* free python mutex */ + /* return the next command to eval */ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/python/pous.xml Wed Jul 29 15:17:10 2009 +0200
@@ -0,0 +1,457 @@
+<?xml version="1.0" encoding="UTF-8"?> +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://www.plcopen.org/xml/tc6.xsd" + xmlns:xhtml="http://www.w3.org/1999/xhtml" + xsi:schemaLocation="http://www.plcopen.org/xml/tc6.xsd"> + <fileHeader companyName="LOLITECH" + creationDateTime="2008-12-14T16:53:26"/> + <contentHeader name="Beremiz non-standard POUs library" + modificationDateTime="2008-12-23T22:35:46"> + <pou name="python_eval" pouType="functionBlock"> + <variable name="RESULT"> + <variable name="STATE"> + <variable name="BUFFER"> + <variable name="PREBUFFER"> + <variable name="TRIGM1"> + <variable name="TRIGGED"> +<![CDATA[{extern void __PythonEvalFB(int, PYTHON_EVAL*);__PythonEvalFB(0, data__);}]]> + <pou name="python_poll" pouType="functionBlock"> + <variable name="RESULT"> + <variable name="STATE"> + <variable name="BUFFER"> + <variable name="PREBUFFER"> + <variable name="TRIGM1"> + <variable name="TRIGGED"> +<![CDATA[{extern void __PythonEvalFB(int, PYTHON_EVAL*);__PythonEvalFB(1,(PYTHON_EVAL*)(void*)data__);}]]> + <pou name="python_gear" pouType="functionBlock"> + <variable name="RESULT"> + <variable name="py_eval"> + <derived name="python_eval"/> + <variable name="COUNTER"> + <block localId="1" width="125" height="110" typeName="python_eval" instanceName="py_eval"> + <position x="525" y="580"/> + <variable formalParameter="TRIG"> + <relPosition x="0" y="40"/> + <connection refLocalId="7" formalParameter="OUT"> + <position x="525" y="620"/> + <position x="495" y="620"/> + <position x="495" y="575"/> + <position x="465" y="575"/> + <variable formalParameter="CODE"> + <relPosition x="0" y="85"/> + <connection refLocalId="4"> + <position x="525" y="665"/> + <position x="370" y="665"/> + <variable formalParameter="ACK"> + <relPosition x="125" y="40"/> + <variable formalParameter="RESULT"> + <relPosition x="125" y="85"/> + <inVariable localId="2" height="35" width="25"> + <position x="435" y="270"/> + <relPosition x="25" y="15"/> + <expression>N</expression> + <inVariable localId="3" height="35" width="55"> + <position x="275" y="585"/> + <relPosition x="55" y="15"/> + <expression>TRIG</expression> + <inVariable localId="4" height="35" width="55"> + <position x="315" y="650"/> + <relPosition x="55" y="15"/> + <expression>CODE</expression> + <outVariable localId="5" height="35" width="45"> + <position x="740" y="605"/> + <relPosition x="0" y="15"/> + <connection refLocalId="1" formalParameter="ACK"> + <position x="740" y="620"/> + <position x="650" y="620"/> + <expression>ACK</expression> + <outVariable localId="6" height="35" width="75"> + <position x="740" y="650"/> + <relPosition x="0" y="15"/> + <connection refLocalId="1" formalParameter="RESULT"> + <position x="740" y="665"/> + <position x="650" y="665"/> + <expression>RESULT</expression> + <block localId="7" width="80" height="65" typeName="AND"> + <position x="385" y="545"/> + <variable formalParameter="IN1"> + <relPosition x="0" y="30"/> + <connection refLocalId="13" formalParameter="OUT"> + <position x="385" y="575"/> + <position x="335" y="575"/> + <position x="335" y="455"/> + <position x="625" y="455"/> + <position x="625" y="285"/> + <position x="615" y="285"/> + <variable formalParameter="IN2"> + <relPosition x="0" y="55"/> + <connection refLocalId="3"> + <position x="385" y="600"/> + <position x="330" y="600"/> + <variable formalParameter="OUT"> + <relPosition x="80" y="30"/> + <inVariable localId="9" height="35" width="85"> + <position x="240" y="330"/> + <relPosition x="85" y="15"/> + <expression>COUNTER</expression> + <block localId="10" width="80" height="65" typeName="ADD"> + <position x="380" y="330"/> + <variable formalParameter="IN1"> + <relPosition x="0" y="30"/> + <connection refLocalId="9"> + <position x="380" y="360"/> + <position x="352" y="360"/> + <position x="352" y="345"/> + <position x="325" y="345"/> + <variable formalParameter="IN2"> + <relPosition x="0" y="55"/> + <connection refLocalId="11"> + <position x="380" y="385"/> + <position x="325" y="385"/> + <variable formalParameter="OUT"> + <relPosition x="80" y="30"/> + <inVariable localId="11" height="35" width="85"> + <position x="240" y="370"/> + <relPosition x="85" y="15"/> + <expression>USINT#1</expression> + <block localId="13" width="80" height="65" typeName="EQ"> + <position x="535" y="255"/> + <variable formalParameter="IN1"> + <relPosition x="0" y="30"/> + <connection refLocalId="2"> + <position x="535" y="285"/> + <position x="460" y="285"/> + <variable formalParameter="IN2"> + <relPosition x="0" y="55"/> + <connection refLocalId="10" formalParameter="OUT"> + <position x="535" y="310"/> + <position x="497" y="310"/> + <position x="497" y="360"/> + <position x="460" y="360"/> + <variable formalParameter="OUT"> + <relPosition x="80" y="30"/> + <block localId="15" width="80" height="135" typeName="SEL"> + <position x="785" y="245"/> + <variable formalParameter="G"> + <relPosition x="0" y="40"/> + <connection refLocalId="13" formalParameter="OUT"> + <position x="785" y="285"/> + <position x="615" y="285"/> + <variable formalParameter="IN0"> + <relPosition x="0" y="75"/> + <connection refLocalId="10" formalParameter="OUT"> + <position x="785" y="320"/> + <position x="650" y="320"/> + <position x="650" y="360"/> + <position x="460" y="360"/> + <variable formalParameter="IN1"> + <relPosition x="0" y="115"/> + <connection refLocalId="16"> + <position x="785" y="360"/> + <position x="760" y="360"/> + <variable formalParameter="OUT"> + <relPosition x="80" y="40"/> + <inVariable localId="16" height="35" width="85"> + <position x="675" y="345"/> + <relPosition x="85" y="15"/> + <expression>USINT#0</expression> + <outVariable localId="17" height="35" width="85"> + <position x="905" y="270"/> + <relPosition x="0" y="15"/> + <connection refLocalId="15" formalParameter="OUT"> + <position x="905" y="285"/> + <position x="865" y="285"/> + <expression>COUNTER</expression> --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/python/python.py Wed Jul 29 15:17:10 2009 +0200
@@ -0,0 +1,280 @@
+from plugger import PlugTemplate, opjimg +from PythonEditor import PythonEditorFrame +from xml.dom import minidom +PythonClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "python_xsd.xsd")) +#------------------------------------------------------------------------------- +# Undo Buffer for PythonCode +#------------------------------------------------------------------------------- +Class implementing a buffer of changes made on the current editing model + # Constructor initialising buffer + def __init__(self, currentstate, issaved = False): + # if current state is defined + # Initialising buffer with currentstate at the first place + for i in xrange(UNDO_BUFFER_LENGTH): + self.Buffer.append(currentstate) + self.Buffer.append(None) + # Initialising index of state saved + # Add a new state in buffer + def Buffering(self, currentstate): + self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH + self.Buffer[self.CurrentIndex] = currentstate + # Actualising buffer limits + self.MaxIndex = self.CurrentIndex + if self.MinIndex == self.CurrentIndex: + # If the removed state was the state saved, there is no state saved in the buffer + if self.LastSave == self.MinIndex: + self.MinIndex = (self.MinIndex + 1) % UNDO_BUFFER_LENGTH + self.MinIndex = max(self.MinIndex, 0) + # Return current state of buffer + return self.Buffer[self.CurrentIndex] + # Change current state to previous in buffer and return new current state + if self.CurrentIndex != self.MinIndex: + self.CurrentIndex = (self.CurrentIndex - 1) % UNDO_BUFFER_LENGTH + return self.Buffer[self.CurrentIndex] + # Change current state to next in buffer and return new current state + if self.CurrentIndex != self.MaxIndex: + self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH + return self.Buffer[self.CurrentIndex] + # Return True if current state is the first in buffer + return self.CurrentIndex == self.MinIndex + # Return True if current state is the last in buffer + return self.CurrentIndex == self.MaxIndex + # Note that current state is saved + def CurrentSaved(self): + self.LastSave = self.CurrentIndex + # Return True if current state is saved + def IsCurrentSaved(self): + return self.LastSave == self.CurrentIndex +class PythonCodeTemplate: + self.PluginMethods.insert(0, + {"bitmap" : opjimg("editPYTHONcode"), + "name" : _("Edit Python File"), + "tooltip" : _("Edit Python File"), + "method" : "_OpenView"}, + filepath = self.PythonFileName() + self.PythonCode = PythonClasses["Python"]() + self.PythonBuffer = UndoBuffer(self.Copy(self.PythonCode), False) + if os.path.isfile(filepath): + xmlfile = open(filepath, 'r') + tree = minidom.parse(xmlfile) + for child in tree.childNodes: + if child.nodeType == tree.ELEMENT_NODE and child.nodeName == "Python": + self.PythonCode.loadXMLTree(child, ["xmlns", "xmlns:xsi", "xsi:schemaLocation"]) + self.PythonBuffer = UndoBuffer(self.Copy(self.PythonCode), True) + return os.path.join(self.PlugParent.PluginPath(), "modules", self.PlugType) + def PythonFileName(self): + return os.path.join(self.PlugPath(), "python.xml") + if self.PythonBuffer.IsCurrentSaved(): + def SetPythonCode(self, text): + self.PythonCode.settext(text) + def GetPythonCode(self): + return self.PythonCode.gettext() + self.GetPlugRoot().SaveProject() + self._View = PythonEditorFrame(self.GetPlugRoot().AppFrame, self) + self._View._onclose = _onclose + self._View._onsave = _onsave + filepath = self.PythonFileName() + text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + extras = {"xmlns":"http://www.w3.org/2001/XMLSchema", + "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi:schemaLocation" : "python_xsd.xsd"} + text += self.PythonCode.generateXMLText("Python", 0, extras) + xmlfile = open(filepath,"w") + self.PythonBuffer.CurrentSaved() +#------------------------------------------------------------------------------- +# Current Buffering Management Functions +#------------------------------------------------------------------------------- + Return a copy of the project + return cPickle.loads(cPickle.dumps(model)) + def BufferPython(self): + self.PythonBuffer.Buffering(self.Copy(self.PythonCode)) + def StartBuffering(self): + self.PythonBuffer.Buffering(self.PythonCode) + def EndBuffering(self): + self.PythonCode = self.Copy(self.PythonCode) + def PythonCodeIsSaved(self): + return self.PythonBuffer.IsCurrentSaved() + def LoadPrevious(self): + self.PythonCode = self.Copy(self.PythonBuffer.Previous()) + self.PythonCode = self.Copy(self.PythonBuffer.Next()) + def GetBufferState(self): + first = self.PythonBuffer.IsFirst() + last = self.PythonBuffer.IsLast() + return not first, not last +def _GetClassFunction(name): + __import__("plugins.python.modules." + name) + return getattr(modules, name).RootClass +class RootClass(PythonCodeTemplate): + # For root object, available Childs Types are modules of the modules packages. + PlugChildsTypes = [(name, _GetClassFunction(name), help) for name, help in zip(modules.__all__,modules.helps)] + return os.path.join(self.PlugParent.PluginPath(), self.PlugType) + def PlugGenerate_C(self, buildpath, locations): + @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5) + @param locations: List of complete variables locations \ + [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...) + "NAME" : name of the variable (generally "__IW0_1_2" style) + "DIR" : direction "Q","I" or "M" + "SIZE" : size "X", "B", "W", "D", "L" + "LOC" : tuple of interger for IEC location (0,1,2,...) + @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND + current_location = self.GetCurrentLocation() + # define a unique name for the generated C file + location_str = "_".join(map(lambda x:str(x), current_location)) + plugin_root = self.GetPlugRoot() + plugin_root.GetIECProgramsAndVariables() + plc_python_filepath = os.path.join(os.path.split(__file__)[0], "plc_python.c") + plc_python_file = open(plc_python_filepath, 'r') + plc_python_code = plc_python_file.read() + plc_python_file.close() + python_eval_fb_list = [] + for v in plugin_root._VariablesList : + if v["vartype"] == "FB" and v["type"] in ["PYTHON_EVAL","PYTHON_POLL"]: + python_eval_fb_list.append(v) + python_eval_fb_count = max(1, len(python_eval_fb_list)) + plc_python_code = plc_python_code % { + "python_eval_fb_count": python_eval_fb_count, + "location": location_str} + Gen_Pythonfile_path = os.path.join(buildpath, "python_%s.c"%location_str) + pythonfile = open(Gen_Pythonfile_path,'w') + pythonfile.write(plc_python_code) + runtimefile_path = os.path.join(buildpath, "runtime_%s.py"%location_str) + runtimefile = open(runtimefile_path, 'w') + runtimefile.write(self.GetPythonCode()) +def _runtime_%(location)s_begin(): +def _runtime_%(location)s_cleanup(): + print "runtime_cleanup" +""" % {"location": location_str}) + if wx.Platform == '__WXMSW__': + matiec_flags = " -I../../matiec/lib" + matiec_flags = " -I../matiec/lib" + return [(Gen_Pythonfile_path, matiec_flags)], "", True, ("runtime_%s.py"%location_str, file(runtimefile_path,"rb")) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/python/python_xsd.xsd Wed Jul 29 15:17:10 2009 +0200
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?> +<xsd:schema targetNamespace="python_xsd.xsd" + xmlns:cext="python_xsd.xsd" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" + attributeFormDefault="unqualified"> + <xsd:element name="Python"> + <xsd:documentation>Formatted text according to parts of XHTML 1.1</xsd:documentation> + <xsd:any namespace="http://www.w3.org/1999/xhtml" processContents="lax"/> --- a/pous.xml Wed Jul 29 10:49:31 2009 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,457 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns="http://www.plcopen.org/xml/tc6.xsd"
- xmlns:xhtml="http://www.w3.org/1999/xhtml"
- xsi:schemaLocation="http://www.plcopen.org/xml/tc6.xsd">
- <fileHeader companyName="LOLITECH"
- creationDateTime="2008-12-14T16:53:26"/>
- <contentHeader name="Beremiz non-standard POUs library"
- modificationDateTime="2008-12-23T22:35:46">
- <pou name="python_eval" pouType="functionBlock">
- <variable name="RESULT">
- <variable name="STATE">
- <variable name="BUFFER">
- <variable name="PREBUFFER">
- <variable name="TRIGM1">
- <variable name="TRIGGED">
-<![CDATA[{extern void __PythonEvalFB(int, PYTHON_EVAL*);__PythonEvalFB(0, data__);}]]>
- <pou name="python_poll" pouType="functionBlock">
- <variable name="RESULT">
- <variable name="STATE">
- <variable name="BUFFER">
- <variable name="PREBUFFER">
- <variable name="TRIGM1">
- <variable name="TRIGGED">
-<![CDATA[{extern void __PythonEvalFB(int, PYTHON_EVAL*);__PythonEvalFB(1,(PYTHON_EVAL*)(void*)data__);}]]>
- <pou name="python_gear" pouType="functionBlock">
- <variable name="RESULT">
- <variable name="py_eval">
- <derived name="python_eval"/>
- <variable name="COUNTER">
- <block localId="1" width="125" height="110" typeName="python_eval" instanceName="py_eval">
- <position x="525" y="580"/>
- <variable formalParameter="TRIG">
- <relPosition x="0" y="40"/>
- <connection refLocalId="7" formalParameter="OUT">
- <position x="525" y="620"/>
- <position x="495" y="620"/>
- <position x="495" y="575"/>
- <position x="465" y="575"/>
- <variable formalParameter="CODE">
- <relPosition x="0" y="85"/>
- <connection refLocalId="4">
- <position x="525" y="665"/>
- <position x="370" y="665"/>
- <variable formalParameter="ACK">
- <relPosition x="125" y="40"/>
- <variable formalParameter="RESULT">
- <relPosition x="125" y="85"/>
- <inVariable localId="2" height="35" width="25">
- <position x="435" y="270"/>
- <relPosition x="25" y="15"/>
- <expression>N</expression>
- <inVariable localId="3" height="35" width="55">
- <position x="275" y="585"/>
- <relPosition x="55" y="15"/>
- <expression>TRIG</expression>
- <inVariable localId="4" height="35" width="55">
- <position x="315" y="650"/>
- <relPosition x="55" y="15"/>
- <expression>CODE</expression>
- <outVariable localId="5" height="35" width="45">
- <position x="740" y="605"/>
- <relPosition x="0" y="15"/>
- <connection refLocalId="1" formalParameter="ACK">
- <position x="740" y="620"/>
- <position x="650" y="620"/>
- <expression>ACK</expression>
- <outVariable localId="6" height="35" width="75">
- <position x="740" y="650"/>
- <relPosition x="0" y="15"/>
- <connection refLocalId="1" formalParameter="RESULT">
- <position x="740" y="665"/>
- <position x="650" y="665"/>
- <expression>RESULT</expression>
- <block localId="7" width="80" height="65" typeName="AND">
- <position x="385" y="545"/>
- <variable formalParameter="IN1">
- <relPosition x="0" y="30"/>
- <connection refLocalId="13" formalParameter="OUT">
- <position x="385" y="575"/>
- <position x="335" y="575"/>
- <position x="335" y="455"/>
- <position x="625" y="455"/>
- <position x="625" y="285"/>
- <position x="615" y="285"/>
- <variable formalParameter="IN2">
- <relPosition x="0" y="55"/>
- <connection refLocalId="3">
- <position x="385" y="600"/>
- <position x="330" y="600"/>
- <variable formalParameter="OUT">
- <relPosition x="80" y="30"/>
- <inVariable localId="9" height="35" width="85">
- <position x="240" y="330"/>
- <relPosition x="85" y="15"/>
- <expression>COUNTER</expression>
- <block localId="10" width="80" height="65" typeName="ADD">
- <position x="380" y="330"/>
- <variable formalParameter="IN1">
- <relPosition x="0" y="30"/>
- <connection refLocalId="9">
- <position x="380" y="360"/>
- <position x="352" y="360"/>
- <position x="352" y="345"/>
- <position x="325" y="345"/>
- <variable formalParameter="IN2">
- <relPosition x="0" y="55"/>
- <connection refLocalId="11">
- <position x="380" y="385"/>
- <position x="325" y="385"/>
- <variable formalParameter="OUT">
- <relPosition x="80" y="30"/>
- <inVariable localId="11" height="35" width="85">
- <position x="240" y="370"/>
- <relPosition x="85" y="15"/>
- <expression>USINT#1</expression>
- <block localId="13" width="80" height="65" typeName="EQ">
- <position x="535" y="255"/>
- <variable formalParameter="IN1">
- <relPosition x="0" y="30"/>
- <connection refLocalId="2">
- <position x="535" y="285"/>
- <position x="460" y="285"/>
- <variable formalParameter="IN2">
- <relPosition x="0" y="55"/>
- <connection refLocalId="10" formalParameter="OUT">
- <position x="535" y="310"/>
- <position x="497" y="310"/>
- <position x="497" y="360"/>
- <position x="460" y="360"/>
- <variable formalParameter="OUT">
- <relPosition x="80" y="30"/>
- <block localId="15" width="80" height="135" typeName="SEL">
- <position x="785" y="245"/>
- <variable formalParameter="G">
- <relPosition x="0" y="40"/>
- <connection refLocalId="13" formalParameter="OUT">
- <position x="785" y="285"/>
- <position x="615" y="285"/>
- <variable formalParameter="IN0">
- <relPosition x="0" y="75"/>
- <connection refLocalId="10" formalParameter="OUT">
- <position x="785" y="320"/>
- <position x="650" y="320"/>
- <position x="650" y="360"/>
- <position x="460" y="360"/>
- <variable formalParameter="IN1">
- <relPosition x="0" y="115"/>
- <connection refLocalId="16">
- <position x="785" y="360"/>
- <position x="760" y="360"/>
- <variable formalParameter="OUT">
- <relPosition x="80" y="40"/>
- <inVariable localId="16" height="35" width="85">
- <position x="675" y="345"/>
- <relPosition x="85" y="15"/>
- <expression>USINT#0</expression>
- <outVariable localId="17" height="35" width="85">
- <position x="905" y="270"/>
- <relPosition x="0" y="15"/>
- <connection refLocalId="15" formalParameter="OUT">
- <position x="905" y="285"/>
- <position x="865" y="285"/>
- <expression>COUNTER</expression>
--- a/runtime/PLCObject.py Wed Jul 29 10:49:31 2009 +0200
+++ b/runtime/PLCObject.py Wed Jul 29 15:17:10 2009 +0200
@@ -53,6 +53,7 @@
self.PLCStatus = "Stopped"
self.PLClibraryHandle = None
self.PLClibraryLock = Lock()
+ self.DummyIteratorLock = None # Creates fake C funcs proxies
@@ -93,10 +94,30 @@
self._startPLC.restype = ctypes.c_int
self._startPLC.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
- self.PLClibraryLock.acquire()
- self.PLClibraryHandle.stopPLC()
- self.PLClibraryLock.release()
+ self.DummyIteratorLock = Lock() + self.DummyIteratorLock.acquire() + self._PythonIterator = getattr(self.PLClibraryHandle, "PythonIterator", None) + if self._PythonIterator is not None: + self._PythonIterator.restype = ctypes.c_char_p + self._PythonIterator.argtypes = [ctypes.c_char_p] + self.PLClibraryLock.acquire() + self.PLClibraryHandle.stopPLC() + self.PLClibraryLock.release() + def DummyIterator(res): + self.DummyIteratorLock.acquire() + self._PythonIterator = DummyIterator + self.PLClibraryLock.acquire() + self.PLClibraryHandle.stopPLC() + self.DummyIteratorLock.release() + self.PLClibraryLock.release() self._stopPLC = StopPLCLock
self._stopPLC.restype = None
@@ -123,10 +144,6 @@
self._resumeDebug = self.PLClibraryHandle.resumeDebug
self._resumeDebug.restype = None
- self._PythonIterator = self.PLClibraryHandle.PythonIterator
- self._PythonIterator.restype = ctypes.c_char_p
- self._PythonIterator.argtypes = [ctypes.c_char_p]
@@ -188,59 +205,78 @@
def PrepareRuntimePy(self):
self.python_threads_vars = globals().copy()
self.python_threads_vars["WorkingDir"] = self.workingdir
- pyfile = os.path.join(self.workingdir, "runtime.py")
- hmifile = os.path.join(self.workingdir, "hmi.py")
- if os.path.exists(hmifile):
- execfile(hmifile, self.python_threads_vars)
- if os.path.exists(pyfile):
- # TODO handle exceptions in runtime.py
- # pyfile may redefine _runtime_cleanup
- # or even call _PythonThreadProc itself.
- execfile(pyfile, self.python_threads_vars)
- PLCprint(traceback.format_exc())
- if self.python_threads_vars.has_key('wx'):
- wx = self.python_threads_vars['wx']
- # try to instanciate the first frame found.
- for name, obj in self.python_threads_vars.iteritems():
- if type(obj)==type(type) and issubclass(obj,wx.Frame):
- self.hmi_frame = obj(None)
- self.python_threads_vars[name] = self.hmi_frame
- # keep track of class... never know
- self.python_threads_vars['Class_'+name] = obj
- self.hmi_frame.Bind(wx.EVT_CLOSE, OnCloseFrame)
- wx.MessageBox(_("Please stop PLC to close"))
- PLCprint(traceback.format_exc())
- elif os.path.exists(pyfile):
- # TODO handle exceptions in runtime.py
- # pyfile may redefine _runtime_cleanup
- # or even call _PythonThreadProc itself.
- execfile(pyfile, self.python_threads_vars)
- PLCprint(traceback.format_exc())
- runtime_begin = self.python_threads_vars.get("_runtime_begin",None)
- if runtime_begin is not None:
+ self.python_threads_vars["_runtime_begin"] = [] + self.python_threads_vars["_runtime_cleanup"] = [] +# pyfile = os.path.join(self.workingdir, "runtime.py") +# hmifile = os.path.join(self.workingdir, "hmi.py") +# if os.path.exists(hmifile): +# execfile(hmifile, self.python_threads_vars) +# if os.path.exists(pyfile): +# # TODO handle exceptions in runtime.py +# # pyfile may redefine _runtime_cleanup +# # or even call _PythonThreadProc itself. +# execfile(pyfile, self.python_threads_vars) +# PLCprint(traceback.format_exc()) +# if self.python_threads_vars.has_key('wx'): +# wx = self.python_threads_vars['wx'] +# # try to instanciate the first frame found. +# for name, obj in self.python_threads_vars.iteritems(): +# if type(obj)==type(type) and issubclass(obj,wx.Frame): +# self.hmi_frame = obj(None) +# self.python_threads_vars[name] = self.hmi_frame +# # keep track of class... never know +# self.python_threads_vars['Class_'+name] = obj +# self.hmi_frame.Bind(wx.EVT_CLOSE, OnCloseFrame) +# def OnCloseFrame(evt): +# wx.MessageBox(_("Please stop PLC to close")) +# PLCprint(traceback.format_exc()) +# elif os.path.exists(pyfile): +# # TODO handle exceptions in runtime.py +# # pyfile may redefine _runtime_cleanup +# # or even call _PythonThreadProc itself. +# execfile(pyfile, self.python_threads_vars) +# PLCprint(traceback.format_exc()) + for filename in os.listdir(self.workingdir): + name, ext = os.path.splitext(filename) + if name.startswith("runtime") and ext == ".py": + # TODO handle exceptions in runtime.py + # pyfile may redefine _runtime_cleanup + # or even call _PythonThreadProc itself. + execfile(os.path.join(self.workingdir, filename), self.python_threads_vars) + PLCprint(traceback.format_exc()) + runtime_begin = self.python_threads_vars.get("_%s_begin" % name, None) + if runtime_begin is not None: + self.python_threads_vars["_runtime_begin"].append(runtime_begin) + runtime_cleanup = self.python_threads_vars.get("_%s_cleanup" % name, None) + if runtime_cleanup is not None: + self.python_threads_vars["_runtime_cleanup"].append(runtime_cleanup) + for runtime_begin in self.python_threads_vars.get("_runtime_begin", []): def FinishRuntimePy(self):
- if self.python_threads_vars is not None:
- runtime_cleanup = self.python_threads_vars.get("_runtime_cleanup",None)
- if runtime_cleanup is not None:
- if self.hmi_frame is not None:
- self.hmi_frame.Destroy()
+ for runtime_cleanup in self.python_threads_vars.get("_runtime_cleanup", []): +# if self.python_threads_vars is not None: +# runtime_cleanup = self.python_threads_vars.get("_runtime_cleanup",None) +# if runtime_cleanup is not None: +# if self.hmi_frame is not None: +# self.hmi_frame.Destroy() self.python_threads_vars = None
def PythonThreadProc(self, debug):
--- a/targets/plc_common_main.c Wed Jul 29 10:49:31 2009 +0200
+++ b/targets/plc_common_main.c Wed Jul 29 15:17:10 2009 +0200
@@ -18,11 +18,6 @@
/*void __retrieve_debug(void);*/
void __publish_debug(void);
-void __init_python(void);
-void __cleanup_python(void);
-void __retrieve_python(void);
-void __publish_python(void);
* Variables used by generated C softPLC and plugins
@@ -47,14 +42,10 @@
@@ -71,7 +62,6 @@
setlocale(LC_NUMERIC, "C");
@@ -82,7 +72,6 @@
--- a/targets/plc_python.c Wed Jul 29 10:49:31 2009 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,213 +0,0 @@
- * Python Asynchronous execution code
- * PLC put python commands in a fifo, respecting execution order
- * with the help of C pragmas inserted in python_eval FB code
- * Buffer content is read asynchronously, (from non real time part),
- * commands are executed and result stored for later use by PLC.
- * In this implementation, fifo is a list of pointer to python_eval
- * function blocks structures. Some local variables have been added in
- * python_eval interface. We use those local variables as buffer and state
-#include "iec_types_all.h"
-/* The fifo (fixed size, as number of FB is fixed) */
-static PYTHON_EVAL* EvalFBs[%(python_eval_fb_count)d];
-/* Producer and consumer cursors */
-static int Current_PLC_EvalFB;
-static int Current_Python_EvalFB;
-/* A global IEC-Python gateway state, for use inside python_eval FBs*/
-#define PYTHON_LOCKED_BY_PYTHON 0
-#define PYTHON_LOCKED_BY_PLC 1
-#define PYTHON_MUSTWAKEUP 2
-#define PYTHON_FINISHED 4
-/* Each python_eval FunctionBlock have it own state */
-#define PYTHON_FB_FREE 0
-#define PYTHON_FB_REQUESTED 1
-#define PYTHON_FB_PROCESSING 2
-#define PYTHON_FB_ANSWERED 3
-int WaitPythonCommands(void);
-void UnBlockPythonCommands(void);
-int TryLockPython(void);
-void UnLockPython(void);
- /* Initialize cursors */
- Current_Python_EvalFB = 0;
- Current_PLC_EvalFB = 0;
- PythonState = PYTHON_LOCKED_BY_PYTHON;
- for(i = 0; i < %(python_eval_fb_count)d; i++)
- PythonState = PYTHON_FINISHED;
- UnBlockPythonCommands();
-void __retrieve_python()
- /* Check Python thread is not being
- * modifying internal python_eval data */
- PythonState = TryLockPython() ?
- PYTHON_LOCKED_BY_PYTHON;
- /* If python thread _is_ in, then PythonState remains PYTHON_LOCKED_BY_PYTHON
- * and python_eval will no do anything */
- if(PythonState & PYTHON_LOCKED_BY_PLC){
- /* If runnig PLC did push something in the fifo*/
- if(PythonState & PYTHON_MUSTWAKEUP){
- /* WakeUp python thread */
- UnBlockPythonCommands();
- * Called by the PLC, each time a python_eval
- * FB instance is executed
-void __PythonEvalFB(int poll, PYTHON_EVAL* data__)
- /* detect rising edge on TRIG to trigger evaluation */
- if(((data__->TRIG && !data__->TRIGM1) ||
- /* polling is equivalent to trig on value rather than on rising edge*/
- (poll && data__->TRIG )) &&
- /* trig only if not already trigged */
- /* make a safe copy of the code */
- data__->PREBUFFER = data__->CODE;
- /* retain value for next rising edge detection */
- data__->TRIGM1 = data__->TRIG;
- /* python thread is not in ? */
- if( PythonState & PYTHON_LOCKED_BY_PLC){
- /* if some answer are waiting, publish*/
- if(data__->STATE == PYTHON_FB_ANSWERED){
- /* Copy buffer content into result*/
- data__->RESULT = data__->BUFFER;
- /* signal result presece to PLC*/
- data__->STATE = PYTHON_FB_FREE;
- /* mark as not trigged */
- /*printf("__PythonEvalFB pop %%d - %%*s\n",Current_PLC_EvalFB, data__->BUFFER.len, data__->BUFFER.body);*/
- /* when in polling, no answer == ack down */
- /* got the order to act ?*/
- if(data__->TRIGGED == 1 &&
- /* and not already being processed */
- data__->STATE == PYTHON_FB_FREE)
- /* Enter the block in the fifo
- /* Don't have to check if fifo cell is free
- * as fifo size == FB count, and a FB cannot
- * be requested twice */
- EvalFBs[Current_PLC_EvalFB] = data__;
- /* copy into BUFFER local*/
- data__->BUFFER = data__->PREBUFFER;
- /* Set ACK pin to low so that we can set a rising edge on result */
- /* when not polling, a new answer imply reseting ack*/
- /* when in polling, acting reset trigger */
- data__->STATE = PYTHON_FB_REQUESTED;
- /* Have to wakeup python thread in case he was asleep */
- PythonState |= PYTHON_MUSTWAKEUP;
- /*printf("__PythonEvalFB push %%d - %%*s\n",Current_PLC_EvalFB, data__->BUFFER.len, data__->BUFFER.body);*/
- Current_PLC_EvalFB = (Current_PLC_EvalFB + 1) %% %(python_eval_fb_count)d;
-char* PythonIterator(char* result)
- //printf("PythonIterator result %%s\n", result);
- /* take python mutex to prevent changing PLC data while PLC running */
- data__ = EvalFBs[Current_Python_EvalFB];
- if(data__ && /* may be null at first run */
- data__->STATE == PYTHON_FB_PROCESSING){ /* some answer awaited*/
- /* If result not None */
- data__->BUFFER.len = strlen(result);
- /* prevent results overrun */
- if(data__->BUFFER.len > STR_MAX_LEN)
- data__->BUFFER.len = STR_MAX_LEN;
- /* TODO : signal error */
- /* Copy results to buffer */
- strncpy(data__->BUFFER.body, result, data__->BUFFER.len);
- data__->BUFFER.len = 0;
- /* remove block from fifo*/
- EvalFBs[Current_Python_EvalFB] = NULL;
- /* Mark block as answered */
- data__->STATE = PYTHON_FB_ANSWERED;
- Current_Python_EvalFB = (Current_Python_EvalFB + 1) %% %(python_eval_fb_count)d;
- //printf("PythonIterator ++ Current_Python_EvalFB %%d\n", Current_Python_EvalFB);
- /* while next slot is empty */
- while(((data__ = EvalFBs[Current_Python_EvalFB]) == NULL) ||
- /* or doesn't contain command */
- data__->STATE != PYTHON_FB_REQUESTED)
- /* wait next FB to eval */
- //printf("PythonIterator wait\n");
- if(WaitPythonCommands()) return NULL;
- if(PythonState & PYTHON_FINISHED) return NULL;
- /* Mark block as processing */
- data__->STATE = PYTHON_FB_PROCESSING;
- //printf("PythonIterator\n");
- /* make BUFFER a null terminated string */
- data__->BUFFER.body[data__->BUFFER.len] = 0;
- /* next command is BUFFER */
- next_command = data__->BUFFER.body;
- /* free python mutex */
- /* return the next command to eval */
--- a/tests/python/plc.xml Wed Jul 29 10:49:31 2009 +0200
+++ b/tests/python/plc.xml Wed Jul 29 15:17:10 2009 +0200
@@ -8,7 +8,7 @@
creationDateTime="2008-12-14T16:21:19"/>
<contentHeader name="Beremiz Python Support Tests"
- modificationDateTime="2008-12-30T00:26:11">
+ modificationDateTime="2009-07-29T14:39:02"> <pageSize x="1024" y="1024"/>
@@ -466,8 +466,8 @@
<configuration name="conf_pytest">
<resource name="res_pytest">
- <task name="pytest_task" interval="00:00:00.100000" priority="0"/>
- <pouInstance name="pytest_instance" type="main_pytest"/>
+ <task name="pytest_task" interval="t#100ms" priority="0"/> + <pouInstance name="pytest_instance" typeName="main_pytest"/> --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/python/python@python/baseplugin.xml Wed Jul 29 15:17:10 2009 +0200
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?> +<BaseParams Name="python" IEC_Channel="0"/> --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/python/python@python/python.xml Wed Jul 29 15:17:10 2009 +0200
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<Python xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="python_xsd.xsd"> +<![CDATA[import time,sys --- a/tests/python/runtime.py Wed Jul 29 10:49:31 2009 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@