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 @@
-import keyword
-import os
-import wx
-import wx.stc as stc
-
-#----------------------------------------------------------------------
-
-"""
-This Python editor class comes from the wxPython demo, a bit tweaked
-"""
-
-#----------------------------------------------------------------------
-
-
-if wx.Platform == '__WXMSW__':
- faces = { 'times': 'Times New Roman',
- 'mono' : 'Courier New',
- 'helv' : 'Arial',
- 'other': 'Comic Sans MS',
- 'size' : 10,
- 'size2': 8,
- }
-elif wx.Platform == '__WXMAC__':
- faces = { 'times': 'Times New Roman',
- 'mono' : 'Monaco',
- 'helv' : 'Arial',
- 'other': 'Comic Sans MS',
- 'size' : 12,
- 'size2': 10,
- }
-else:
- faces = { 'times': 'Times',
- 'mono' : 'Courier',
- 'helv' : 'Helvetica',
- 'other': 'new century schoolbook',
- 'size' : 12,
- 'size2': 10,
- }
-
-
-#----------------------------------------------------------------------
-
-class PythonSTC(stc.StyledTextCtrl):
-
- fold_symbols = 2
-
- def __init__(self, parent, ID,
- pos=wx.DefaultPosition, size=wx.DefaultSize,
- style=0):
- 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.SetMargins(0,0)
-
- self.SetViewWhiteSpace(False)
- #self.SetBufferedDraw(False)
- #self.SetViewEOL(True)
- #self.SetEOLMode(stc.STC_EOL_CRLF)
- #self.SetUseAntiAliasing(True)
-
- self.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
- self.SetEdgeColumn(78)
-
- # 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")
-
- # Python styles
- # Default
- self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
- # Comments
- self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
- # Number
- self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces)
- # String
- self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
- # Single quoted string
- self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
- # Keyword
- self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces)
- # Triple quotes
- self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces)
- # Triple double quotes
- 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)
- # Operators
- self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces)
- # Identifiers
- self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
- # Comment-blocks
- 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():
- self.CallTipCancel()
-
- event.Skip()
-## For later use
-# key = event.GetKeyCode()
-# if key == 32 and event.ControlDown():
-# pos = self.GetCurrentPos()
-#
-# # Tips
-# if event.ShiftDown():
-# 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)')
-# # Code completion
-# else:
-# #lst = []
-# #for x in range(50000):
-# # lst.append('%05d' % x)
-# #st = " ".join(lst)
-# #print len(st)
-# #self.AutoCompShow(0, st)
-#
-# kw = keyword.kwlist[:]
-# kw.append("zzzzzz?2")
-# kw.append("aaaaa?2")
-# 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:
-# kw[i] = kw[i] + "?1"
-#
-# self.AutoCompShow(0, " ".join(kw))
-# else:
-# event.Skip()
-
-
- def OnUpdateUI(self, evt):
- # check for matching braces
- braceAtCaret = -1
- braceOpposite = -1
- charBefore = None
- caretPos = self.GetCurrentPos()
-
- if caretPos > 0:
- charBefore = self.GetCharAt(caretPos - 1)
- styleBefore = self.GetStyleAt(caretPos - 1)
-
- # check before
- if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
- braceAtCaret = caretPos - 1
-
- # check after
- if braceAtCaret < 0:
- charAfter = self.GetCharAt(caretPos)
- styleAfter = self.GetStyleAt(caretPos)
-
- if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
- braceAtCaret = caretPos
-
- if braceAtCaret >= 0:
- braceOpposite = self.BraceMatch(braceAtCaret)
-
- if braceAtCaret != -1 and braceOpposite == -1:
- self.BraceBadLight(braceAtCaret)
- else:
- self.BraceHighlight(braceAtCaret, braceOpposite)
- #pt = self.PointFromPosition(braceOpposite)
- #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
- #print pt
- #self.Refresh(False)
-
-
- def OnMarginClick(self, evt):
- # fold and unfold as needed
- if evt.GetMargin() == 2:
- if evt.GetShift() and evt.GetControl():
- self.FoldAll()
- else:
- lineClicked = self.LineFromPosition(evt.GetPosition())
-
- if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
- if evt.GetShift():
- self.SetFoldExpanded(lineClicked, True)
- self.Expand(lineClicked, True, True, 1)
- elif evt.GetControl():
- if self.GetFoldExpanded(lineClicked):
- self.SetFoldExpanded(lineClicked, False)
- self.Expand(lineClicked, False, True, 0)
- else:
- self.SetFoldExpanded(lineClicked, True)
- self.Expand(lineClicked, True, True, 100)
- else:
- self.ToggleFold(lineClicked)
-
-
- def FoldAll(self):
- lineCount = self.GetLineCount()
- expanding = True
-
- # 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)
- break
-
- lineNum = 0
-
- while lineNum < lineCount:
- level = self.GetFoldLevel(lineNum)
- if level & stc.STC_FOLDLEVELHEADERFLAG and \
- (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
-
- if expanding:
- self.SetFoldExpanded(lineNum, True)
- lineNum = self.Expand(lineNum, True)
- lineNum = lineNum - 1
- else:
- lastChild = self.GetLastChild(lineNum, -1)
- self.SetFoldExpanded(lineNum, False)
-
- if lastChild > lineNum:
- self.HideLines(lineNum+1, lastChild)
-
- lineNum = lineNum + 1
-
-
-
- def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
- lastChild = self.GetLastChild(line, level)
- line = line + 1
-
- while line <= lastChild:
- if force:
- if visLevels > 0:
- self.ShowLines(line, line)
- else:
- self.HideLines(line, line)
- else:
- if doExpand:
- self.ShowLines(line, line)
-
- if level == -1:
- level = self.GetFoldLevel(line)
-
- if level & stc.STC_FOLDLEVELHEADERFLAG:
- if force:
- if visLevels > 1:
- self.SetFoldExpanded(line, True)
- else:
- self.SetFoldExpanded(line, False)
-
- line = self.Expand(line, doExpand, force, visLevels-1)
-
- else:
- if doExpand and self.GetFoldExpanded(line):
- line = self.Expand(line, True, force, visLevels-1)
- else:
- line = self.Expand(line, False, force, visLevels-1)
- else:
- line = line + 1
-
- return line
-
-
-#----------------------------------------------------------------------
-class PythonCodeEditor(PythonSTC):
- def __init__(self, parent):
- PythonSTC.__init__(self, parent, -1, style=wx.BORDER_NONE)
- self.SetUpEditor()
-
- # Some methods to make it compatible with how the wxTextCtrl is used
- def SetValue(self, value):
- if wx.USE_UNICODE:
- value = value.decode('utf-8')
- self.SetText(value)
- self.EmptyUndoBuffer()
- self.SetSavePoint()
-
- def IsModified(self):
- return self.GetModify()
-
- def Clear(self):
- self.ClearAll()
-
- def SetInsertionPoint(self, pos):
- self.SetCurrentPos(pos)
- self.SetAnchor(pos)
-
- def ShowPosition(self, pos):
- line = self.LineFromPosition(pos)
- #self.EnsureVisible(line)
- self.GotoLine(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)
-
- def SetUpEditor(self):
- """
- This method carries out the work of setting up the demo editor.
- It's seperate so as not to clutter up the init code.
- """
- import keyword
-
- self.SetLexer(stc.STC_LEX_PYTHON)
- self.SetKeyWords(0, " ".join(keyword.kwlist))
-
- # Enable folding
- self.SetProperty("fold", "1" )
-
- # Highlight tab/space mixing (shouldn't be any)
- self.SetProperty("tab.timmy.whinge.level", "1")
-
- # Set left and right margins
- self.SetMargins(2,2)
-
- # 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!
- # White space
- 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
- # edit them that way.
- self.SetEOLMode(wx.stc.STC_EOL_LF)
- self.SetViewEOL(False)
-
- # 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")
-
- # Global default style
- 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')
- else:
- 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.
- self.StyleClearAll()
-
- # 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')
- # Highlighted brace
- self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00')
- # Unmatched brace
- self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000')
- # Indentation guide
- self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD")
-
- # Python styles
- self.StyleSetSpec(wx.stc.STC_P_DEFAULT, 'fore:#000000')
- # Comments
- self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, 'fore:#008000,back:#F0FFF0')
- self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0')
- # Numbers
- 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')
- # Keywords
- self.StyleSetSpec(wx.stc.STC_P_WORD, 'fore:#000080,bold')
- # Triple quotes
- self.StyleSetSpec(wx.stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA')
- self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA')
- # Class names
- self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, 'fore:#0000FF,bold')
- # Function names
- self.StyleSetSpec(wx.stc.STC_P_DEFNAME, 'fore:#008080,bold')
- # Operators
- 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')
-
- # Caret color
- self.SetCaretForeground("BLUE")
- # Selection background
- 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.box.Fit(self)
- self.SetSizer(self.box)
-
- self.sourceFile = None
-
- 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.Clear()
- self.editor.SetValue(source)
- self.JumpToLine(0)
- self.btnSave.Enable(False)
-
- def JumpToLine(self, line, highlight=False):
- self.editor.GotoLine(line)
- self.editor.SetFocus()
- if highlight:
- self.editor.SelectLine(line)
-
- def OnCodeModified(self, event):
- self.btnSave.Enable(self.editor.IsModified())
- # TODO : add callback
-
- 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()
- if result == wx.ID_NO:
- return
- dlg.Destroy()
-
- source = self.editor.GetText().encode("utf-8")
-
- f = file(self.sourceFile, "w")
- f.write(source)
- f.close()
-
- # TODO
- #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")
-
def GetLocations(self):
locations = []
filepath = os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h")
@@ -1073,15 +1065,6 @@
else:
return None
- 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__':
- glade = "\"%s\""%glade
- 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 ],
"", # no ldflags
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
- else:
- _hmipyfile = hmipyfile
- self.launch_wxglade(['-o', _hmipyfile, '-g', 'python', wxgfile], wait=True)
- res += (("hmi.py", file(hmipyfile,"rb")),)
-
- return res
-
def ResetIECProgramsAndVariables(self):
"""
@@ -1211,23 +1177,6 @@
return debug_code
- 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))
-
- # prepare debug code
- python_code = targets.code("plc_python") % {
- "python_eval_fb_count": python_eval_fb_count}
- return python_code
-
def Generate_plc_common_main(self):
"""
Use plugins layout given in LocationCFilesAndCFLAGS to
@@ -1334,8 +1283,6 @@
for generator, filename, name in [
# debugger code
(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")]:
try:
@@ -1407,32 +1354,6 @@
new_dialog.Show()
- 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)
-
- new_dialog.Show()
-
- 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>
- <title>frame_1</title>
- </object>
-</application>
-""")
- if wx.Platform == '__WXMSW__':
- wxg_filename = "\"%s\""%wxg_filename
- self.launch_wxglade([wxg_filename])
-
def _EditPLC(self):
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 @@
+import wx, wx.grid
+import wx.stc as stc
+import keyword
+
+if wx.Platform == '__WXMSW__':
+ faces = { 'times': 'Times New Roman',
+ 'mono' : 'Courier New',
+ 'helv' : 'Arial',
+ 'other': 'Comic Sans MS',
+ 'size' : 10,
+ 'size2': 8,
+ }
+elif wx.Platform == '__WXMAC__':
+ faces = { 'times': 'Times New Roman',
+ 'mono' : 'Monaco',
+ 'helv' : 'Arial',
+ 'other': 'Comic Sans MS',
+ 'size' : 12,
+ 'size2': 10,
+ }
+else:
+ faces = { 'times': 'Times',
+ 'mono' : 'Courier',
+ 'helv' : 'Helvetica',
+ 'other': 'new century schoolbook',
+ 'size' : 12,
+ 'size2': 10,
+ }
+
+def AppendMenu(parent, help, id, kind, text):
+ if wx.VERSION >= (2, 6, 0):
+ parent.Append(help=help, id=id, kind=kind, text=text)
+ else:
+ parent.Append(helpString=help, id=id, kind=kind, item=text)
+
+
+[ID_PYTHONEDITOR,
+] = [wx.NewId() for _init_ctrls in range(1)]
+
+def GetCursorPos(old, new):
+ old_length = len(old)
+ new_length = len(new)
+ common_length = min(old_length, new_length)
+ i = 0
+ for i in xrange(common_length):
+ if old[i] != new[i]:
+ break
+ if old_length < new_length:
+ if common_length > 0 and old[i] != new[i]:
+ return i + new_length - old_length
+ else:
+ 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]:
+ return i
+ else:
+ return i + 1
+ else:
+ return None
+
+class PythonEditor(stc.StyledTextCtrl):
+
+ fold_symbols = 3
+
+ def __init__(self, parent, window, controler):
+ stc.StyledTextCtrl.__init__(self, parent, ID_PYTHONEDITOR, wx.DefaultPosition,
+ wx.DefaultSize, 0)
+
+ 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.SetMargins(0,0)
+
+ self.SetViewWhiteSpace(False)
+ #self.SetBufferedDraw(False)
+ #self.SetViewEOL(True)
+ #self.SetEOLMode(stc.STC_EOL_CRLF)
+ #self.SetUseAntiAliasing(True)
+
+ self.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
+ self.SetEdgeColumn(78)
+
+ # 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)
+
+ # Global default style
+ 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')
+ else:
+ 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.
+ self.StyleClearAll()
+
+ # 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')
+ # Highlighted brace
+ self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00')
+ # Unmatched brace
+ self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000')
+ # Indentation guide
+ self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD")
+
+ # Python styles
+ self.StyleSetSpec(wx.stc.STC_P_DEFAULT, 'fore:#000000')
+ # Comments
+ self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, 'fore:#008000,back:#F0FFF0')
+ self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0')
+ # Numbers
+ 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')
+ # Keywords
+ self.StyleSetSpec(wx.stc.STC_P_WORD, 'fore:#000080,bold')
+ # Triple quotes
+ self.StyleSetSpec(wx.stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA')
+ self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA')
+ # Class names
+ self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, 'fore:#0000FF,bold')
+ # Function names
+ self.StyleSetSpec(wx.stc.STC_P_DEFNAME, 'fore:#008080,bold')
+ # Operators
+ 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')
+
+ # Caret color
+ self.SetCaretForeground("BLUE")
+ # Selection background
+ 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())
+ self.RegisterImage(1,
+ wx.ArtProvider.GetBitmap(wx.ART_DELETE, size=(16,16)))
+ self.RegisterImage(2,
+ wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16)))
+ self.RegisterImage(3,
+ 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!
+ # White space
+ 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
+ # edit them that way.
+ self.SetEOLMode(wx.stc.STC_EOL_LF)
+ self.SetViewEOL(False)
+
+ # 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:
+ self.StartBuffering()
+ elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1:
+ self.Controler.EndBuffering()
+ self.StartBuffering()
+ self.CurrentAction = ("Add", event.GetPosition())
+ elif mod_type&wx.stc.STC_MOD_BEFOREDELETE:
+ if self.CurrentAction == None:
+ self.StartBuffering()
+ elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1:
+ self.Controler.EndBuffering()
+ self.StartBuffering()
+ self.CurrentAction = ("Delete", event.GetPosition())
+ event.Skip()
+
+ def OnDoDrop(self, event):
+ self.ResetBuffer()
+ wx.CallAfter(self.RefreshModel)
+ event.Skip()
+
+ def IsViewing(self, name):
+ return self.Name == name
+
+ # Buffer the last model state
+ def RefreshBuffer(self):
+ self.Controler.BufferPython()
+ if self.ParentWindow:
+ self.ParentWindow.RefreshTitle()
+ self.ParentWindow.RefreshEditMenu()
+
+ def StartBuffering(self):
+ self.Controler.StartBuffering()
+ if self.ParentWindow:
+ self.ParentWindow.RefreshTitle()
+ self.ParentWindow.RefreshEditMenu()
+
+ def ResetBuffer(self):
+ if self.CurrentAction != None:
+ self.Controler.EndBuffering()
+ self.CurrentAction = None
+
+ def RefreshView(self):
+ self.ResetBuffer()
+ self.DisableEvents = True
+ old_cursor_pos = self.GetCurrentPos()
+ old_text = self.GetText()
+ new_text = self.Controler.GetPythonCode()
+ self.SetText(new_text)
+ new_cursor_pos = GetCursorPos(old_text, new_text)
+ if new_cursor_pos != None:
+ self.GotoPos(new_cursor_pos)
+ else:
+ self.GotoPos(old_cursor_pos)
+ self.ScrollToColumn(0)
+ self.EmptyUndoBuffer()
+ self.DisableEvents = False
+
+ self.Colourise(0, -1)
+
+ def RefreshModel(self):
+ self.Controler.SetPythonCode(self.GetText())
+
+ def OnKeyPressed(self, event):
+ if self.CallTipActive():
+ self.CallTipCancel()
+ key = event.GetKeyCode()
+
+ if key == 32 and event.ControlDown():
+ pos = self.GetCurrentPos()
+
+ # Tips
+ if event.ShiftDown():
+ pass
+## 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)')
+ # Code completion
+ else:
+ 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]))
+ else:
+ wx.CallAfter(self.RefreshModel)
+ event.Skip()
+
+ def OnKillFocus(self, event):
+ self.AutoCompCancel()
+ event.Skip()
+
+ def OnUpdateUI(self, evt):
+ # check for matching braces
+ braceAtCaret = -1
+ braceOpposite = -1
+ charBefore = None
+ caretPos = self.GetCurrentPos()
+
+ if caretPos > 0:
+ charBefore = self.GetCharAt(caretPos - 1)
+ styleBefore = self.GetStyleAt(caretPos - 1)
+
+ # check before
+ if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
+ braceAtCaret = caretPos - 1
+
+ # check after
+ if braceAtCaret < 0:
+ charAfter = self.GetCharAt(caretPos)
+ styleAfter = self.GetStyleAt(caretPos)
+
+ if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
+ braceAtCaret = caretPos
+
+ if braceAtCaret >= 0:
+ braceOpposite = self.BraceMatch(braceAtCaret)
+
+ if braceAtCaret != -1 and braceOpposite == -1:
+ self.BraceBadLight(braceAtCaret)
+ else:
+ self.BraceHighlight(braceAtCaret, braceOpposite)
+ #pt = self.PointFromPosition(braceOpposite)
+ #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
+ #print pt
+ #self.Refresh(False)
+
+
+ def OnMarginClick(self, evt):
+ # fold and unfold as needed
+ if evt.GetMargin() == 2:
+ if evt.GetShift() and evt.GetControl():
+ self.FoldAll()
+ else:
+ lineClicked = self.LineFromPosition(evt.GetPosition())
+
+ if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
+ if evt.GetShift():
+ self.SetFoldExpanded(lineClicked, True)
+ self.Expand(lineClicked, True, True, 1)
+ elif evt.GetControl():
+ if self.GetFoldExpanded(lineClicked):
+ self.SetFoldExpanded(lineClicked, False)
+ self.Expand(lineClicked, False, True, 0)
+ else:
+ self.SetFoldExpanded(lineClicked, True)
+ self.Expand(lineClicked, True, True, 100)
+ else:
+ self.ToggleFold(lineClicked)
+
+
+ def FoldAll(self):
+ lineCount = self.GetLineCount()
+ expanding = True
+
+ # 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)
+ break
+
+ lineNum = 0
+
+ while lineNum < lineCount:
+ level = self.GetFoldLevel(lineNum)
+ if level & stc.STC_FOLDLEVELHEADERFLAG and \
+ (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
+
+ if expanding:
+ self.SetFoldExpanded(lineNum, True)
+ lineNum = self.Expand(lineNum, True)
+ lineNum = lineNum - 1
+ else:
+ lastChild = self.GetLastChild(lineNum, -1)
+ self.SetFoldExpanded(lineNum, False)
+
+ if lastChild > lineNum:
+ self.HideLines(lineNum+1, lastChild)
+
+ lineNum = lineNum + 1
+
+
+
+ def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
+ lastChild = self.GetLastChild(line, level)
+ line = line + 1
+
+ while line <= lastChild:
+ if force:
+ if visLevels > 0:
+ self.ShowLines(line, line)
+ else:
+ self.HideLines(line, line)
+ else:
+ if doExpand:
+ self.ShowLines(line, line)
+
+ if level == -1:
+ level = self.GetFoldLevel(line)
+
+ if level & stc.STC_FOLDLEVELHEADERFLAG:
+ if force:
+ if visLevels > 1:
+ self.SetFoldExpanded(line, True)
+ else:
+ self.SetFoldExpanded(line, False)
+
+ line = self.Expand(line, doExpand, force, visLevels-1)
+
+ else:
+ if doExpand and self.GetFoldExpanded(line):
+ line = self.Expand(line, True, force, visLevels-1)
+ else:
+ line = self.Expand(line, False, force, visLevels-1)
+ else:
+ line = line + 1
+
+ return line
+
+
+#-------------------------------------------------------------------------------
+# PythonEditor Main Frame Class
+#-------------------------------------------------------------------------------
+
+
+[ID_PYTHONEDITORFRAME,
+] = [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):
+ if id is not None:
+ event(self, id, function)
+ else:
+ event(self, 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')
+
+ def _init_utils(self):
+ 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._init_utils()
+ 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)
+ else:
+ 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()
+ self.RefreshTitle()
+ self.RefreshEditMenu()
+
+ def OnCloseFrame(self, event):
+ if wx.VERSION >= (2, 8, 0):
+ self.AUIManager.UnInit()
+ if getattr(self, "_onclose", None) is not None:
+ self._onclose()
+ event.Skip()
+
+ def OnSaveMenu(self, event):
+ if getattr(self, "_onsave", None) != None:
+ self._onsave()
+ self.RefreshTitle()
+ self.RefreshEditMenu()
+ event.Skip()
+
+ 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()
+ event.Skip()
+
+ def OnUndoMenu(self, event):
+ self.Controler.LoadPrevious()
+ self.PythonEdited.RefreshView()
+ self.RefreshTitle()
+ self.RefreshEditMenu()
+ event.Skip()
+
+ def OnRedoMenu(self, event):
+ self.Controler.LoadNext()
+ self.PythonEdited.RefreshView()
+ self.RefreshTitle()
+ self.RefreshEditMenu()
+ event.Skip()
+
--- /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 @@
+Asynchronous
\ 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 @@
+from python import *
--- /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("__")]
+
+helps = []
+for name in __all__:
+ helpfilename = path.join(_base_path, name, "README")
+ if path.isfile(helpfilename):
+ helps.append(open(helpfilename).readline().strip())
+ else:
+ helps.append(name)
--- /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
+ * flags.
+ *
+ * */
+
+#include "iec_types_all.h"
+#include "POUS.h"
+#include <string.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*/
+static int PythonState;
+#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);
+void LockPython(void);
+
+int __init_%(location)s()
+{
+ int i;
+ /* 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++)
+ EvalFBs[i] = NULL;
+ return 0;
+}
+
+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_PLC :
+ 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();
+ }
+ UnLockPython();
+ }
+}
+/**
+ * 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 */
+ data__->TRIGGED == 0){
+ /* mark as trigged */
+ data__->TRIGGED = 1;
+ /* 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__->ACK = 1;
+ /* Mark as free */
+ data__->STATE = PYTHON_FB_FREE;
+ /* mark as not trigged */
+ if(!poll)
+ data__->TRIGGED = 0;
+ /*printf("__PythonEvalFB pop %%d - %%*s\n",Current_PLC_EvalFB, data__->BUFFER.len, data__->BUFFER.body);*/
+ }else if(poll){
+ /* when in polling, no answer == ack down */
+ data__->ACK = 0;
+ }
+ /* 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 */
+ if(!poll){
+ /* when not polling, a new answer imply reseting ack*/
+ data__->ACK = 0;
+ }else{
+ /* when in polling, acting reset trigger */
+ data__->TRIGGED = 0;
+ }
+ /* Mark FB busy */
+ 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);*/
+ /* Get a new line */
+ Current_PLC_EvalFB = (Current_PLC_EvalFB + 1) %% %(python_eval_fb_count)d;
+ }
+ }
+}
+
+char* PythonIterator(char* result)
+{
+ char* next_command;
+ PYTHON_EVAL* data__;
+ //printf("PythonIterator result %%s\n", result);
+ /* take python mutex to prevent changing PLC data while PLC running */
+ LockPython();
+ /* Get current FB */
+ 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 */
+ if(result){
+ /* Get results len */
+ 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);
+ }else{
+ data__->BUFFER.len = 0;
+ }
+ /* remove block from fifo*/
+ EvalFBs[Current_Python_EvalFB] = NULL;
+ /* Mark block as answered */
+ data__->STATE = PYTHON_FB_ANSWERED;
+ /* Get a new line */
+ 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)
+ {
+ UnLockPython();
+ /* wait next FB to eval */
+ //printf("PythonIterator wait\n");
+ if(WaitPythonCommands()) return NULL;
+ /*emergency exit*/
+ if(PythonState & PYTHON_FINISHED) return NULL;
+ LockPython();
+ }
+ /* 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 */
+ UnLockPython();
+ /* return the next command to eval */
+ return next_command;
+}
+
--- /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"
+ productName="Beremiz"
+ productVersion="0.0"
+ creationDateTime="2008-12-14T16:53:26"/>
+ <contentHeader name="Beremiz non-standard POUs library"
+ modificationDateTime="2008-12-23T22:35:46">
+ <coordinateInfo>
+ <fbd>
+ <scaling x="0" y="0"/>
+ </fbd>
+ <ld>
+ <scaling x="0" y="0"/>
+ </ld>
+ <sfc>
+ <scaling x="0" y="0"/>
+ </sfc>
+ </coordinateInfo>
+ </contentHeader>
+ <types>
+ <dataTypes/>
+ <pous>
+ <pou name="python_eval" pouType="functionBlock">
+ <interface>
+ <inputVars>
+ <variable name="TRIG">
+ <type>
+ <BOOL/>
+ </type>
+ </variable>
+ <variable name="CODE">
+ <type>
+ <string/>
+ </type>
+ </variable>
+ </inputVars>
+ <outputVars>
+ <variable name="ACK">
+ <type>
+ <BOOL/>
+ </type>
+ </variable>
+ <variable name="RESULT">
+ <type>
+ <string/>
+ </type>
+ </variable>
+ </outputVars>
+ <localVars>
+ <variable name="STATE">
+ <type>
+ <DWORD/>
+ </type>
+ </variable>
+ <variable name="BUFFER">
+ <type>
+ <string/>
+ </type>
+ </variable>
+ <variable name="PREBUFFER">
+ <type>
+ <string/>
+ </type>
+ </variable>
+ <variable name="TRIGM1">
+ <type>
+ <BOOL/>
+ </type>
+ </variable>
+ <variable name="TRIGGED">
+ <type>
+ <BOOL/>
+ </type>
+ </variable>
+ </localVars>
+ </interface>
+ <body>
+ <ST>
+<![CDATA[{extern void __PythonEvalFB(int, PYTHON_EVAL*);__PythonEvalFB(0, data__);}]]>
+ </ST>
+ </body>
+ </pou>
+ <pou name="python_poll" pouType="functionBlock">
+ <interface>
+ <inputVars>
+ <variable name="TRIG">
+ <type>
+ <BOOL/>
+ </type>
+ </variable>
+ <variable name="CODE">
+ <type>
+ <string/>
+ </type>
+ </variable>
+ </inputVars>
+ <outputVars>
+ <variable name="ACK">
+ <type>
+ <BOOL/>
+ </type>
+ </variable>
+ <variable name="RESULT">
+ <type>
+ <string/>
+ </type>
+ </variable>
+ </outputVars>
+ <localVars>
+ <variable name="STATE">
+ <type>
+ <DWORD/>
+ </type>
+ </variable>
+ <variable name="BUFFER">
+ <type>
+ <string/>
+ </type>
+ </variable>
+ <variable name="PREBUFFER">
+ <type>
+ <string/>
+ </type>
+ </variable>
+ <variable name="TRIGM1">
+ <type>
+ <BOOL/>
+ </type>
+ </variable>
+ <variable name="TRIGGED">
+ <type>
+ <BOOL/>
+ </type>
+ </variable>
+ </localVars>
+ </interface>
+ <body>
+ <ST>
+<![CDATA[{extern void __PythonEvalFB(int, PYTHON_EVAL*);__PythonEvalFB(1,(PYTHON_EVAL*)(void*)data__);}]]>
+ </ST>
+ </body>
+ </pou>
+ <pou name="python_gear" pouType="functionBlock">
+ <interface>
+ <inputVars>
+ <variable name="N">
+ <type>
+ <USINT/>
+ </type>
+ </variable>
+ <variable name="TRIG">
+ <type>
+ <BOOL/>
+ </type>
+ </variable>
+ <variable name="CODE">
+ <type>
+ <string/>
+ </type>
+ </variable>
+ </inputVars>
+ <outputVars>
+ <variable name="ACK">
+ <type>
+ <BOOL/>
+ </type>
+ </variable>
+ <variable name="RESULT">
+ <type>
+ <string/>
+ </type>
+ </variable>
+ </outputVars>
+ <localVars>
+ <variable name="py_eval">
+ <type>
+ <derived name="python_eval"/>
+ </type>
+ </variable>
+ <variable name="COUNTER">
+ <type>
+ <USINT/>
+ </type>
+ </variable>
+ </localVars>
+ </interface>
+ <body>
+ <FBD>
+ <block localId="1" width="125" height="110" typeName="python_eval" instanceName="py_eval">
+ <position x="525" y="580"/>
+ <inputVariables>
+ <variable formalParameter="TRIG">
+ <connectionPointIn>
+ <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"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="CODE">
+ <connectionPointIn>
+ <relPosition x="0" y="85"/>
+ <connection refLocalId="4">
+ <position x="525" y="665"/>
+ <position x="370" y="665"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="ACK">
+ <connectionPointOut>
+ <relPosition x="125" y="40"/>
+ </connectionPointOut>
+ </variable>
+ <variable formalParameter="RESULT">
+ <connectionPointOut>
+ <relPosition x="125" y="85"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <inVariable localId="2" height="35" width="25">
+ <position x="435" y="270"/>
+ <connectionPointOut>
+ <relPosition x="25" y="15"/>
+ </connectionPointOut>
+ <expression>N</expression>
+ </inVariable>
+ <inVariable localId="3" height="35" width="55">
+ <position x="275" y="585"/>
+ <connectionPointOut>
+ <relPosition x="55" y="15"/>
+ </connectionPointOut>
+ <expression>TRIG</expression>
+ </inVariable>
+ <inVariable localId="4" height="35" width="55">
+ <position x="315" y="650"/>
+ <connectionPointOut>
+ <relPosition x="55" y="15"/>
+ </connectionPointOut>
+ <expression>CODE</expression>
+ </inVariable>
+ <outVariable localId="5" height="35" width="45">
+ <position x="740" y="605"/>
+ <connectionPointIn>
+ <relPosition x="0" y="15"/>
+ <connection refLocalId="1" formalParameter="ACK">
+ <position x="740" y="620"/>
+ <position x="650" y="620"/>
+ </connection>
+ </connectionPointIn>
+ <expression>ACK</expression>
+ </outVariable>
+ <outVariable localId="6" height="35" width="75">
+ <position x="740" y="650"/>
+ <connectionPointIn>
+ <relPosition x="0" y="15"/>
+ <connection refLocalId="1" formalParameter="RESULT">
+ <position x="740" y="665"/>
+ <position x="650" y="665"/>
+ </connection>
+ </connectionPointIn>
+ <expression>RESULT</expression>
+ </outVariable>
+ <block localId="7" width="80" height="65" typeName="AND">
+ <position x="385" y="545"/>
+ <inputVariables>
+ <variable formalParameter="IN1">
+ <connectionPointIn>
+ <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"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN2">
+ <connectionPointIn>
+ <relPosition x="0" y="55"/>
+ <connection refLocalId="3">
+ <position x="385" y="600"/>
+ <position x="330" y="600"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="80" y="30"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <inVariable localId="9" height="35" width="85">
+ <position x="240" y="330"/>
+ <connectionPointOut>
+ <relPosition x="85" y="15"/>
+ </connectionPointOut>
+ <expression>COUNTER</expression>
+ </inVariable>
+ <block localId="10" width="80" height="65" typeName="ADD">
+ <position x="380" y="330"/>
+ <inputVariables>
+ <variable formalParameter="IN1">
+ <connectionPointIn>
+ <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"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN2">
+ <connectionPointIn>
+ <relPosition x="0" y="55"/>
+ <connection refLocalId="11">
+ <position x="380" y="385"/>
+ <position x="325" y="385"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="80" y="30"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <inVariable localId="11" height="35" width="85">
+ <position x="240" y="370"/>
+ <connectionPointOut>
+ <relPosition x="85" y="15"/>
+ </connectionPointOut>
+ <expression>USINT#1</expression>
+ </inVariable>
+ <block localId="13" width="80" height="65" typeName="EQ">
+ <position x="535" y="255"/>
+ <inputVariables>
+ <variable formalParameter="IN1">
+ <connectionPointIn>
+ <relPosition x="0" y="30"/>
+ <connection refLocalId="2">
+ <position x="535" y="285"/>
+ <position x="460" y="285"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN2">
+ <connectionPointIn>
+ <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"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="80" y="30"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <block localId="15" width="80" height="135" typeName="SEL">
+ <position x="785" y="245"/>
+ <inputVariables>
+ <variable formalParameter="G">
+ <connectionPointIn>
+ <relPosition x="0" y="40"/>
+ <connection refLocalId="13" formalParameter="OUT">
+ <position x="785" y="285"/>
+ <position x="615" y="285"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN0">
+ <connectionPointIn>
+ <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"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN1">
+ <connectionPointIn>
+ <relPosition x="0" y="115"/>
+ <connection refLocalId="16">
+ <position x="785" y="360"/>
+ <position x="760" y="360"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="80" y="40"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <inVariable localId="16" height="35" width="85">
+ <position x="675" y="345"/>
+ <connectionPointOut>
+ <relPosition x="85" y="15"/>
+ </connectionPointOut>
+ <expression>USINT#0</expression>
+ </inVariable>
+ <outVariable localId="17" height="35" width="85">
+ <position x="905" y="270"/>
+ <connectionPointIn>
+ <relPosition x="0" y="15"/>
+ <connection refLocalId="15" formalParameter="OUT">
+ <position x="905" y="285"/>
+ <position x="865" y="285"/>
+ </connection>
+ </connectionPointIn>
+ <expression>COUNTER</expression>
+ </outVariable>
+ </FBD>
+ </body>
+ </pou>
+ </pous>
+ </types>
+ <instances>
+ <configurations/>
+ </instances>
+</project>
--- /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 @@
+import wx
+import os
+import modules
+from plugger import PlugTemplate, opjimg
+from PythonEditor import PythonEditorFrame
+
+from xml.dom import minidom
+from xmlclass import *
+import cPickle
+
+PythonClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "python_xsd.xsd"))
+
+#-------------------------------------------------------------------------------
+# Undo Buffer for PythonCode
+#-------------------------------------------------------------------------------
+
+# Length of the buffer
+UNDO_BUFFER_LENGTH = 20
+
+"""
+Class implementing a buffer of changes made on the current editing model
+"""
+class UndoBuffer:
+
+ # Constructor initialising buffer
+ def __init__(self, currentstate, issaved = False):
+ self.Buffer = []
+ self.CurrentIndex = -1
+ self.MinIndex = -1
+ self.MaxIndex = -1
+ # if current state is defined
+ if currentstate:
+ self.CurrentIndex = 0
+ self.MinIndex = 0
+ self.MaxIndex = 0
+ # Initialising buffer with currentstate at the first place
+ for i in xrange(UNDO_BUFFER_LENGTH):
+ if i == 0:
+ self.Buffer.append(currentstate)
+ else:
+ self.Buffer.append(None)
+ # Initialising index of state saved
+ if issaved:
+ self.LastSave = 0
+ else:
+ self.LastSave = -1
+
+ # 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.LastSave = -1
+ self.MinIndex = (self.MinIndex + 1) % UNDO_BUFFER_LENGTH
+ self.MinIndex = max(self.MinIndex, 0)
+
+ # Return current state of buffer
+ def Current(self):
+ return self.Buffer[self.CurrentIndex]
+
+ # Change current state to previous in buffer and return new current state
+ def Previous(self):
+ if self.CurrentIndex != self.MinIndex:
+ self.CurrentIndex = (self.CurrentIndex - 1) % UNDO_BUFFER_LENGTH
+ return self.Buffer[self.CurrentIndex]
+ return None
+
+ # Change current state to next in buffer and return new current state
+ def Next(self):
+ if self.CurrentIndex != self.MaxIndex:
+ self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH
+ return self.Buffer[self.CurrentIndex]
+ return None
+
+ # Return True if current state is the first in buffer
+ def IsFirst(self):
+ return self.CurrentIndex == self.MinIndex
+
+ # Return True if current state is the last in buffer
+ def IsLast(self):
+ 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:
+
+ def __init__(self):
+
+ self.PluginMethods.insert(0,
+ {"bitmap" : opjimg("editPYTHONcode"),
+ "name" : _("Edit Python File"),
+ "tooltip" : _("Edit Python File"),
+ "method" : "_OpenView"},
+ )
+
+ filepath = self.PythonFileName()
+
+ self.Buffering = False
+ 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)
+ xmlfile.close()
+
+ 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)
+ else:
+ self.OnPlugSave()
+
+ def PluginPath(self):
+ return os.path.join(self.PlugParent.PluginPath(), "modules", self.PlugType)
+
+ def PythonFileName(self):
+ return os.path.join(self.PlugPath(), "python.xml")
+
+ def GetFilename(self):
+ if self.PythonBuffer.IsCurrentSaved():
+ return "python"
+ else:
+ return "~python~"
+
+ def SetPythonCode(self, text):
+ self.PythonCode.settext(text)
+
+ def GetPythonCode(self):
+ return self.PythonCode.gettext()
+
+ _View = None
+ def _OpenView(self):
+ if not self._View:
+ def _onclose():
+ self._View = None
+ def _onsave():
+ self.GetPlugRoot().SaveProject()
+ self._View = PythonEditorFrame(self.GetPlugRoot().AppFrame, self)
+ self._View._onclose = _onclose
+ self._View._onsave = _onsave
+ self._View.Show()
+
+ def OnPlugSave(self):
+ 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")
+ xmlfile.write(text)
+ xmlfile.close()
+
+ self.PythonBuffer.CurrentSaved()
+ return True
+
+#-------------------------------------------------------------------------------
+# Current Buffering Management Functions
+#-------------------------------------------------------------------------------
+
+ """
+ Return a copy of the project
+ """
+ def Copy(self, model):
+ return cPickle.loads(cPickle.dumps(model))
+
+ def BufferPython(self):
+ self.PythonBuffer.Buffering(self.Copy(self.PythonCode))
+
+ def StartBuffering(self):
+ self.PythonBuffer.Buffering(self.PythonCode)
+ self.Buffering = True
+
+ def EndBuffering(self):
+ if self.Buffering:
+ self.PythonCode = self.Copy(self.PythonCode)
+ self.Buffering = False
+
+ def PythonCodeIsSaved(self):
+ if self.PythonBuffer:
+ return self.PythonBuffer.IsCurrentSaved()
+ else:
+ return True
+
+ def LoadPrevious(self):
+ self.PythonCode = self.Copy(self.PythonBuffer.Previous())
+
+ def LoadNext(self):
+ 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):
+ def GetRootClass():
+ __import__("plugins.python.modules." + name)
+ return getattr(modules, name).RootClass
+ return GetRootClass
+
+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)]
+
+ def PluginPath(self):
+ return os.path.join(self.PlugParent.PluginPath(), self.PlugType)
+
+ def PlugGenerate_C(self, buildpath, locations):
+ """
+ Generate C code
+ @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))
+
+ # prepare python code
+ 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)
+ pythonfile.close()
+
+ runtimefile_path = os.path.join(buildpath, "runtime_%s.py"%location_str)
+ runtimefile = open(runtimefile_path, 'w')
+ runtimefile.write(self.GetPythonCode())
+ runtimefile.write("""
+def _runtime_%(location)s_begin():
+ print "runtime_begin"
+
+def _runtime_%(location)s_cleanup():
+ print "runtime_cleanup"
+
+""" % {"location": location_str})
+ runtimefile.close()
+
+ if wx.Platform == '__WXMSW__':
+ matiec_flags = " -I../../matiec/lib"
+ else:
+ 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:complexType>
+ <xsd:annotation>
+ <xsd:documentation>Formatted text according to parts of XHTML 1.1</xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:any namespace="http://www.w3.org/1999/xhtml" processContents="lax"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+</xsd:schema>
--- 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"
- productName="Beremiz"
- productVersion="0.0"
- creationDateTime="2008-12-14T16:53:26"/>
- <contentHeader name="Beremiz non-standard POUs library"
- modificationDateTime="2008-12-23T22:35:46">
- <coordinateInfo>
- <fbd>
- <scaling x="0" y="0"/>
- </fbd>
- <ld>
- <scaling x="0" y="0"/>
- </ld>
- <sfc>
- <scaling x="0" y="0"/>
- </sfc>
- </coordinateInfo>
- </contentHeader>
- <types>
- <dataTypes/>
- <pous>
- <pou name="python_eval" pouType="functionBlock">
- <interface>
- <inputVars>
- <variable name="TRIG">
- <type>
- <BOOL/>
- </type>
- </variable>
- <variable name="CODE">
- <type>
- <string/>
- </type>
- </variable>
- </inputVars>
- <outputVars>
- <variable name="ACK">
- <type>
- <BOOL/>
- </type>
- </variable>
- <variable name="RESULT">
- <type>
- <string/>
- </type>
- </variable>
- </outputVars>
- <localVars>
- <variable name="STATE">
- <type>
- <DWORD/>
- </type>
- </variable>
- <variable name="BUFFER">
- <type>
- <string/>
- </type>
- </variable>
- <variable name="PREBUFFER">
- <type>
- <string/>
- </type>
- </variable>
- <variable name="TRIGM1">
- <type>
- <BOOL/>
- </type>
- </variable>
- <variable name="TRIGGED">
- <type>
- <BOOL/>
- </type>
- </variable>
- </localVars>
- </interface>
- <body>
- <ST>
-<![CDATA[{extern void __PythonEvalFB(int, PYTHON_EVAL*);__PythonEvalFB(0, data__);}]]>
- </ST>
- </body>
- </pou>
- <pou name="python_poll" pouType="functionBlock">
- <interface>
- <inputVars>
- <variable name="TRIG">
- <type>
- <BOOL/>
- </type>
- </variable>
- <variable name="CODE">
- <type>
- <string/>
- </type>
- </variable>
- </inputVars>
- <outputVars>
- <variable name="ACK">
- <type>
- <BOOL/>
- </type>
- </variable>
- <variable name="RESULT">
- <type>
- <string/>
- </type>
- </variable>
- </outputVars>
- <localVars>
- <variable name="STATE">
- <type>
- <DWORD/>
- </type>
- </variable>
- <variable name="BUFFER">
- <type>
- <string/>
- </type>
- </variable>
- <variable name="PREBUFFER">
- <type>
- <string/>
- </type>
- </variable>
- <variable name="TRIGM1">
- <type>
- <BOOL/>
- </type>
- </variable>
- <variable name="TRIGGED">
- <type>
- <BOOL/>
- </type>
- </variable>
- </localVars>
- </interface>
- <body>
- <ST>
-<![CDATA[{extern void __PythonEvalFB(int, PYTHON_EVAL*);__PythonEvalFB(1,(PYTHON_EVAL*)(void*)data__);}]]>
- </ST>
- </body>
- </pou>
- <pou name="python_gear" pouType="functionBlock">
- <interface>
- <inputVars>
- <variable name="N">
- <type>
- <USINT/>
- </type>
- </variable>
- <variable name="TRIG">
- <type>
- <BOOL/>
- </type>
- </variable>
- <variable name="CODE">
- <type>
- <string/>
- </type>
- </variable>
- </inputVars>
- <outputVars>
- <variable name="ACK">
- <type>
- <BOOL/>
- </type>
- </variable>
- <variable name="RESULT">
- <type>
- <string/>
- </type>
- </variable>
- </outputVars>
- <localVars>
- <variable name="py_eval">
- <type>
- <derived name="python_eval"/>
- </type>
- </variable>
- <variable name="COUNTER">
- <type>
- <USINT/>
- </type>
- </variable>
- </localVars>
- </interface>
- <body>
- <FBD>
- <block localId="1" width="125" height="110" typeName="python_eval" instanceName="py_eval">
- <position x="525" y="580"/>
- <inputVariables>
- <variable formalParameter="TRIG">
- <connectionPointIn>
- <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"/>
- </connection>
- </connectionPointIn>
- </variable>
- <variable formalParameter="CODE">
- <connectionPointIn>
- <relPosition x="0" y="85"/>
- <connection refLocalId="4">
- <position x="525" y="665"/>
- <position x="370" y="665"/>
- </connection>
- </connectionPointIn>
- </variable>
- </inputVariables>
- <inOutVariables/>
- <outputVariables>
- <variable formalParameter="ACK">
- <connectionPointOut>
- <relPosition x="125" y="40"/>
- </connectionPointOut>
- </variable>
- <variable formalParameter="RESULT">
- <connectionPointOut>
- <relPosition x="125" y="85"/>
- </connectionPointOut>
- </variable>
- </outputVariables>
- </block>
- <inVariable localId="2" height="35" width="25">
- <position x="435" y="270"/>
- <connectionPointOut>
- <relPosition x="25" y="15"/>
- </connectionPointOut>
- <expression>N</expression>
- </inVariable>
- <inVariable localId="3" height="35" width="55">
- <position x="275" y="585"/>
- <connectionPointOut>
- <relPosition x="55" y="15"/>
- </connectionPointOut>
- <expression>TRIG</expression>
- </inVariable>
- <inVariable localId="4" height="35" width="55">
- <position x="315" y="650"/>
- <connectionPointOut>
- <relPosition x="55" y="15"/>
- </connectionPointOut>
- <expression>CODE</expression>
- </inVariable>
- <outVariable localId="5" height="35" width="45">
- <position x="740" y="605"/>
- <connectionPointIn>
- <relPosition x="0" y="15"/>
- <connection refLocalId="1" formalParameter="ACK">
- <position x="740" y="620"/>
- <position x="650" y="620"/>
- </connection>
- </connectionPointIn>
- <expression>ACK</expression>
- </outVariable>
- <outVariable localId="6" height="35" width="75">
- <position x="740" y="650"/>
- <connectionPointIn>
- <relPosition x="0" y="15"/>
- <connection refLocalId="1" formalParameter="RESULT">
- <position x="740" y="665"/>
- <position x="650" y="665"/>
- </connection>
- </connectionPointIn>
- <expression>RESULT</expression>
- </outVariable>
- <block localId="7" width="80" height="65" typeName="AND">
- <position x="385" y="545"/>
- <inputVariables>
- <variable formalParameter="IN1">
- <connectionPointIn>
- <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"/>
- </connection>
- </connectionPointIn>
- </variable>
- <variable formalParameter="IN2">
- <connectionPointIn>
- <relPosition x="0" y="55"/>
- <connection refLocalId="3">
- <position x="385" y="600"/>
- <position x="330" y="600"/>
- </connection>
- </connectionPointIn>
- </variable>
- </inputVariables>
- <inOutVariables/>
- <outputVariables>
- <variable formalParameter="OUT">
- <connectionPointOut>
- <relPosition x="80" y="30"/>
- </connectionPointOut>
- </variable>
- </outputVariables>
- </block>
- <inVariable localId="9" height="35" width="85">
- <position x="240" y="330"/>
- <connectionPointOut>
- <relPosition x="85" y="15"/>
- </connectionPointOut>
- <expression>COUNTER</expression>
- </inVariable>
- <block localId="10" width="80" height="65" typeName="ADD">
- <position x="380" y="330"/>
- <inputVariables>
- <variable formalParameter="IN1">
- <connectionPointIn>
- <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"/>
- </connection>
- </connectionPointIn>
- </variable>
- <variable formalParameter="IN2">
- <connectionPointIn>
- <relPosition x="0" y="55"/>
- <connection refLocalId="11">
- <position x="380" y="385"/>
- <position x="325" y="385"/>
- </connection>
- </connectionPointIn>
- </variable>
- </inputVariables>
- <inOutVariables/>
- <outputVariables>
- <variable formalParameter="OUT">
- <connectionPointOut>
- <relPosition x="80" y="30"/>
- </connectionPointOut>
- </variable>
- </outputVariables>
- </block>
- <inVariable localId="11" height="35" width="85">
- <position x="240" y="370"/>
- <connectionPointOut>
- <relPosition x="85" y="15"/>
- </connectionPointOut>
- <expression>USINT#1</expression>
- </inVariable>
- <block localId="13" width="80" height="65" typeName="EQ">
- <position x="535" y="255"/>
- <inputVariables>
- <variable formalParameter="IN1">
- <connectionPointIn>
- <relPosition x="0" y="30"/>
- <connection refLocalId="2">
- <position x="535" y="285"/>
- <position x="460" y="285"/>
- </connection>
- </connectionPointIn>
- </variable>
- <variable formalParameter="IN2">
- <connectionPointIn>
- <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"/>
- </connection>
- </connectionPointIn>
- </variable>
- </inputVariables>
- <inOutVariables/>
- <outputVariables>
- <variable formalParameter="OUT">
- <connectionPointOut>
- <relPosition x="80" y="30"/>
- </connectionPointOut>
- </variable>
- </outputVariables>
- </block>
- <block localId="15" width="80" height="135" typeName="SEL">
- <position x="785" y="245"/>
- <inputVariables>
- <variable formalParameter="G">
- <connectionPointIn>
- <relPosition x="0" y="40"/>
- <connection refLocalId="13" formalParameter="OUT">
- <position x="785" y="285"/>
- <position x="615" y="285"/>
- </connection>
- </connectionPointIn>
- </variable>
- <variable formalParameter="IN0">
- <connectionPointIn>
- <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"/>
- </connection>
- </connectionPointIn>
- </variable>
- <variable formalParameter="IN1">
- <connectionPointIn>
- <relPosition x="0" y="115"/>
- <connection refLocalId="16">
- <position x="785" y="360"/>
- <position x="760" y="360"/>
- </connection>
- </connectionPointIn>
- </variable>
- </inputVariables>
- <inOutVariables/>
- <outputVariables>
- <variable formalParameter="OUT">
- <connectionPointOut>
- <relPosition x="80" y="40"/>
- </connectionPointOut>
- </variable>
- </outputVariables>
- </block>
- <inVariable localId="16" height="35" width="85">
- <position x="675" y="345"/>
- <connectionPointOut>
- <relPosition x="85" y="15"/>
- </connectionPointOut>
- <expression>USINT#0</expression>
- </inVariable>
- <outVariable localId="17" height="35" width="85">
- <position x="905" y="270"/>
- <connectionPointIn>
- <relPosition x="0" y="15"/>
- <connection refLocalId="15" formalParameter="OUT">
- <position x="905" y="285"/>
- <position x="865" y="285"/>
- </connection>
- </connectionPointIn>
- <expression>COUNTER</expression>
- </outVariable>
- </FBD>
- </body>
- </pou>
- </pous>
- </types>
- <instances>
- <configurations/>
- </instances>
-</project>
--- 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
self._FreePLC()
self.daemon = daemon
@@ -93,10 +94,30 @@
self._startPLC.restype = ctypes.c_int
self._startPLC.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
- def StopPLCLock():
- 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]
+
+ def StopPLCLock():
+ self.PLClibraryLock.acquire()
+ self.PLClibraryHandle.stopPLC()
+ self.PLClibraryLock.release()
+
+ else:
+ def DummyIterator(res):
+ self.DummyIteratorLock.acquire()
+ return None
+ self._PythonIterator = DummyIterator
+
+ def StopPLCLock():
+ 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]
return True
except:
@@ -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):
- try:
- execfile(hmifile, self.python_threads_vars)
- if os.path.exists(pyfile):
- try:
- # TODO handle exceptions in runtime.py
- # pyfile may redefine _runtime_cleanup
- # or even call _PythonThreadProc itself.
- execfile(pyfile, self.python_threads_vars)
- except:
- 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():
- # obj is a class
- if type(obj)==type(type) and issubclass(obj,wx.Frame):
- def create_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)
- self.hmi_frame.Show()
-
- def OnCloseFrame(evt):
- wx.MessageBox(_("Please stop PLC to close"))
- create_frame()
- break
- except:
- PLCprint(traceback.format_exc())
- elif os.path.exists(pyfile):
- try:
- # TODO handle exceptions in runtime.py
- # pyfile may redefine _runtime_cleanup
- # or even call _PythonThreadProc itself.
- execfile(pyfile, self.python_threads_vars)
- except:
- 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):
+# try:
+# execfile(hmifile, self.python_threads_vars)
+# if os.path.exists(pyfile):
+# try:
+# # TODO handle exceptions in runtime.py
+# # pyfile may redefine _runtime_cleanup
+# # or even call _PythonThreadProc itself.
+# execfile(pyfile, self.python_threads_vars)
+# except:
+# 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():
+# # obj is a class
+# if type(obj)==type(type) and issubclass(obj,wx.Frame):
+# def create_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)
+# self.hmi_frame.Show()
+#
+# def OnCloseFrame(evt):
+# wx.MessageBox(_("Please stop PLC to close"))
+# create_frame()
+# break
+# except:
+# PLCprint(traceback.format_exc())
+# elif os.path.exists(pyfile):
+# try:
+# # TODO handle exceptions in runtime.py
+# # pyfile may redefine _runtime_cleanup
+# # or even call _PythonThreadProc itself.
+# execfile(pyfile, self.python_threads_vars)
+# except:
+# PLCprint(traceback.format_exc())
+ for filename in os.listdir(self.workingdir):
+ name, ext = os.path.splitext(filename)
+ if name.startswith("runtime") and ext == ".py":
+ try:
+ # 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)
+ except:
+ 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", []):
runtime_begin()
def FinishRuntimePy(self):
- runtime_cleanup = None
- if self.python_threads_vars is not None:
- runtime_cleanup = self.python_threads_vars.get("_runtime_cleanup",None)
- if runtime_cleanup is not None:
- runtime_cleanup()
- if self.hmi_frame is not None:
- self.hmi_frame.Destroy()
+ for runtime_cleanup in self.python_threads_vars.get("_runtime_cleanup", []):
+ 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:
+# runtime_cleanup()
+# 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 @@
%(retrieve_calls)s
- __retrieve_python();
-
/*__retrieve_debug();*/
config_run__(__tick);
- __publish_python();
-
__publish_debug();
%(publish_calls)s
@@ -71,7 +62,6 @@
setlocale(LC_NUMERIC, "C");
config_init__();
__init_debug();
- __init_python();
%(init_calls)s
return 0;
}
@@ -82,7 +72,6 @@
{
%(cleanup_calls)s
__cleanup_debug();
- __cleanup_python();
}
--- 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
- * flags.
- *
- * */
-
-#include "iec_types_all.h"
-#include "POUS.h"
-#include <string.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*/
-static int PythonState;
-#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);
-void LockPython(void);
-
-void __init_python()
-{
- int i;
- /* 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++)
- EvalFBs[i] = NULL;
-}
-
-void __cleanup_python()
-{
- PythonState = PYTHON_FINISHED;
- UnBlockPythonCommands();
-}
-
-void __retrieve_python()
-{
- /* Check Python thread is not being
- * modifying internal python_eval data */
- PythonState = TryLockPython() ?
- PYTHON_LOCKED_BY_PLC :
- 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_python()
-{
- if(PythonState & PYTHON_LOCKED_BY_PLC){
- /* If runnig PLC did push something in the fifo*/
- if(PythonState & PYTHON_MUSTWAKEUP){
- /* WakeUp python thread */
- UnBlockPythonCommands();
- }
- UnLockPython();
- }
-}
-/**
- * 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 */
- data__->TRIGGED == 0){
- /* mark as trigged */
- data__->TRIGGED = 1;
- /* 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__->ACK = 1;
- /* Mark as free */
- data__->STATE = PYTHON_FB_FREE;
- /* mark as not trigged */
- if(!poll)
- data__->TRIGGED = 0;
- /*printf("__PythonEvalFB pop %%d - %%*s\n",Current_PLC_EvalFB, data__->BUFFER.len, data__->BUFFER.body);*/
- }else if(poll){
- /* when in polling, no answer == ack down */
- data__->ACK = 0;
- }
- /* 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 */
- if(!poll){
- /* when not polling, a new answer imply reseting ack*/
- data__->ACK = 0;
- }else{
- /* when in polling, acting reset trigger */
- data__->TRIGGED = 0;
- }
- /* Mark FB busy */
- 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);*/
- /* Get a new line */
- Current_PLC_EvalFB = (Current_PLC_EvalFB + 1) %% %(python_eval_fb_count)d;
- }
- }
-}
-
-char* PythonIterator(char* result)
-{
- char* next_command;
- PYTHON_EVAL* data__;
- //printf("PythonIterator result %%s\n", result);
- /* take python mutex to prevent changing PLC data while PLC running */
- LockPython();
- /* Get current FB */
- 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 */
- if(result){
- /* Get results len */
- 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);
- }else{
- data__->BUFFER.len = 0;
- }
- /* remove block from fifo*/
- EvalFBs[Current_Python_EvalFB] = NULL;
- /* Mark block as answered */
- data__->STATE = PYTHON_FB_ANSWERED;
- /* Get a new line */
- 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)
- {
- UnLockPython();
- /* wait next FB to eval */
- //printf("PythonIterator wait\n");
- if(WaitPythonCommands()) return NULL;
- /*emergency exit*/
- if(PythonState & PYTHON_FINISHED) return NULL;
- LockPython();
- }
- /* 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 */
- UnLockPython();
- /* return the next command to eval */
- return next_command;
-}
-
--- 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 @@
productVersion="0.0"
creationDateTime="2008-12-14T16:21:19"/>
<contentHeader name="Beremiz Python Support Tests"
- modificationDateTime="2008-12-30T00:26:11">
+ modificationDateTime="2009-07-29T14:39:02">
<coordinateInfo>
<pageSize x="1024" y="1024"/>
<fbd>
@@ -466,8 +466,8 @@
<configurations>
<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"/>
</resource>
</configuration>
</configurations>
--- /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
+def myprintfunc(arg):
+ print arg
+ sys.stdout.flush()
+ return arg]]>
+</Python>
--- 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 @@
-import time,sys
-def myprintfunc(arg):
- print arg
- sys.stdout.flush()
- return arg