--- a/CodeFileTreeNode.py Sun May 12 23:32:30 2013 +0200
+++ b/CodeFileTreeNode.py Mon May 13 14:31:23 2013 +0200
@@ -2,26 +2,82 @@
from xml.dom import minidom
import cPickle
-from xmlclass import *
+from xmlclass import GenerateClassesFromXSDstring, UpdateXMLClassGlobals
from PLCControler import UndoBuffer
-from editors.CodeFileEditor import SECTIONS_NAMES
-CodeFileClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "code_file.xsd"))
+CODEFILE_XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <xsd:element name="%(codefile_name)s">
+ <xsd:complexType>
+ <xsd:sequence>
+ %(includes_section)s
+ <xsd:element name="variables">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="variable" minOccurs="0" maxOccurs="unbounded">
+ <xsd:complexType>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="type" type="xsd:string" use="required"/>
+ <xsd:attribute name="class" use="optional">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="input"/>
+ <xsd:enumeration value="memory"/>
+ <xsd:enumeration value="output"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:attribute>
+ <xsd:attribute name="initial" type="xsd:string" use="optional" default=""/>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ %(sections)s
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:complexType name="CodeText">
+ <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:schema>"""
+
+SECTION_TAG_ELEMENT = "<xsd:element name=\"%s\" type=\"CodeText\"/>"
class CodeFile:
+ CODEFILE_NAME = "CodeFile"
+ SECTIONS_NAMES = []
+
def __init__(self):
+ sections_str = {"codefile_name": self.CODEFILE_NAME}
+ if "includes" in self.SECTIONS_NAMES:
+ sections_str["includes_section"] = SECTION_TAG_ELEMENT % "includes"
+ else:
+ sections_str["includes_section"] = ""
+ sections_str["sections"] = "\n".join(
+ [SECTION_TAG_ELEMENT % name
+ for name in self.SECTIONS_NAMES if name != "includes"])
+
+ self.CodeFileClasses = GenerateClassesFromXSDstring(
+ CODEFILE_XSD % sections_str)
+
filepath = self.CodeFileName()
- self.CodeFile = CodeFileClasses["CodeFile"]()
+ self.CodeFile = self.CodeFileClasses[self.CODEFILE_NAME]()
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 in ["CodeFile", "CFile"]:
+ if child.nodeType == tree.ELEMENT_NODE and child.nodeName in [self.CODEFILE_NAME]:
self.CodeFile.loadXMLTree(child, ["xmlns", "xmlns:xsi", "xsi:schemaLocation"])
self.CreateCodeFileBuffer(True)
else:
@@ -37,7 +93,7 @@
def SetVariables(self, variables):
self.CodeFile.variables.setvariable([])
for var in variables:
- variable = CodeFileClasses["variables_variable"]()
+ variable = self.CodeFileClasses["variables_variable"]()
variable.setname(var["Name"])
variable.settype(var["Type"])
variable.setinitial(var["Initial"])
@@ -46,33 +102,21 @@
def GetVariables(self):
datas = []
for var in self.CodeFile.variables.getvariable():
- datas.append({"Name" : var.getname(), "Type" : var.gettype(), "Initial" : var.getinitial()})
+ datas.append({"Name" : var.getname(),
+ "Type" : var.gettype(),
+ "Initial" : var.getinitial()})
return datas
def SetTextParts(self, parts):
- for section, code_object in zip(
- SECTIONS_NAMES,
- [self.CodeFile.includes,
- self.CodeFile.globals,
- self.CodeFile.initFunction,
- self.CodeFile.cleanUpFunction,
- self.CodeFile.retrieveFunction,
- self.CodeFile.publishFunction]):
- code_object.settext(parts[section])
-
+ for section in self.SECTIONS_NAMES:
+ section_code = parts.get(section)
+ if section_code is not None:
+ getattr(self.CodeFile, section).settext(section_code)
+
def GetTextParts(self):
- parts = {}
- for section, code_object in zip(
- SECTIONS_NAMES,
- [self.CodeFile.includes,
- self.CodeFile.globals,
- self.CodeFile.initFunction,
- self.CodeFile.cleanUpFunction,
- self.CodeFile.retrieveFunction,
- self.CodeFile.publishFunction]):
- parts[section] = code_object.gettext()
- return parts
-
+ return dict([(section, getattr(self.CodeFile, section).gettext())
+ for section in self.SECTIONS_NAMES])
+
def CTNTestModified(self):
return self.ChangesToSave or not self.CodeFileIsSaved()
@@ -80,10 +124,7 @@
filepath = self.CodeFileName()
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" : "code_file.xsd"}
- text += self.CodeFile.generateXMLText("CodeFile", 0, extras)
+ text += self.CodeFile.generateXMLText(self.CODEFILE_NAME, 0)
xmlfile = open(filepath,"w")
xmlfile.write(text.encode("utf-8"))
@@ -103,25 +144,33 @@
# Current Buffering Management Functions
#-------------------------------------------------------------------------------
+ def cPickle_loads(self, str_obj):
+ UpdateXMLClassGlobals(self.CodeFileClasses)
+ return cPickle.loads(str_obj)
+
+ def cPickle_dumps(self, obj):
+ UpdateXMLClassGlobals(self.CodeFileClasses)
+ return cPickle.dumps(obj)
+
"""
Return a copy of the codefile model
"""
def Copy(self, model):
- return cPickle.loads(cPickle.dumps(model))
+ return self.cPickle_loads(self.cPickle_dumps(model))
def CreateCodeFileBuffer(self, saved):
self.Buffering = False
- self.CodeFileBuffer = UndoBuffer(cPickle.dumps(self.CodeFile), saved)
+ self.CodeFileBuffer = UndoBuffer(self.cPickle_dumps(self.CodeFile), saved)
def BufferCodeFile(self):
- self.CodeFileBuffer.Buffering(cPickle.dumps(self.CodeFile))
+ self.CodeFileBuffer.Buffering(self.cPickle_dumps(self.CodeFile))
def StartBuffering(self):
self.Buffering = True
def EndBuffering(self):
if self.Buffering:
- self.CodeFileBuffer.Buffering(cPickle.dumps(self.CodeFile))
+ self.CodeFileBuffer.Buffering(self.cPickle_dumps(self.CodeFile))
self.Buffering = False
def MarkCodeFileAsSaved(self):
@@ -133,10 +182,10 @@
def LoadPrevious(self):
self.EndBuffering()
- self.CodeFile = cPickle.loads(self.CodeFileBuffer.Previous())
+ self.CodeFile = self.cPickle_loads(self.CodeFileBuffer.Previous())
def LoadNext(self):
- self.CodeFile = cPickle.loads(self.CodeFileBuffer.Next())
+ self.CodeFile = self.cPickle_loads(self.CodeFileBuffer.Next())
def GetBufferState(self):
first = self.CodeFileBuffer.IsFirst() and not self.Buffering
--- a/c_ext/c_ext.py Sun May 12 23:32:30 2013 +0200
+++ b/c_ext/c_ext.py Mon May 13 14:31:23 2013 +0200
@@ -1,3 +1,4 @@
+
import os
from CFileEditor import CFileEditor
@@ -14,8 +15,19 @@
</xsd:element>
</xsd:schema>
"""
+ CODEFILE_NAME = "CFile"
+ SECTIONS_NAMES = [
+ "includes",
+ "globals",
+ "initFunction",
+ "cleanUpFunction",
+ "retrieveFunction",
+ "publishFunction"]
EditorType = CFileEditor
+ def GenerateClassesFromXSDstring(self, xsd_string):
+ return GenerateClassesFromXSDstring(xsd_string)
+
def GetIconName(self):
return "Cfile"
--- a/code_file.xsd Sun May 12 23:32:30 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1" ?>
-<xsd:schema targetNamespace="code_file.xsd"
- xmlns:codefile="code_file.xsd"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- elementFormDefault="qualified"
- attributeFormDefault="unqualified">
-
- <xsd:element name="CodeFile">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="includes" type="codefile:CodeText"/>
- <xsd:element name="variables">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="variable" minOccurs="0" maxOccurs="unbounded">
- <xsd:complexType>
- <xsd:attribute name="name" type="xsd:string" use="required"/>
- <xsd:attribute name="type" type="xsd:string" use="required"/>
- <xsd:attribute name="class" use="optional">
- <xsd:simpleType>
- <xsd:restriction base="xsd:string">
- <xsd:enumeration value="input"/>
- <xsd:enumeration value="memory"/>
- <xsd:enumeration value="output"/>
- </xsd:restriction>
- </xsd:simpleType>
- </xsd:attribute>
- <xsd:attribute name="initial" type="xsd:string" use="optional" default=""/>
- </xsd:complexType>
- </xsd:element>
- </xsd:sequence>
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="globals" type="codefile:CodeText"/>
- <xsd:element name="initFunction" type="codefile:CodeText"/>
- <xsd:element name="cleanUpFunction" type="codefile:CodeText"/>
- <xsd:element name="retrieveFunction" type="codefile:CodeText"/>
- <xsd:element name="publishFunction" type="codefile:CodeText"/>
- </xsd:sequence>
- </xsd:complexType>
- </xsd:element>
- <xsd:complexType name="CodeText">
- <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:schema>
--- a/editors/CodeFileEditor.py Sun May 12 23:32:30 2013 +0200
+++ b/editors/CodeFileEditor.py Mon May 13 14:31:23 2013 +0200
@@ -12,9 +12,6 @@
from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos, NAVIGATION_KEYS
from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD
-SECTIONS_NAMES = ["Includes", "Globals", "Init",
- "CleanUp", "Retrieve", "Publish"]
-
[STC_CODE_ERROR, STC_CODE_SEARCH_RESULT,
STC_CODE_SECTION] = range(15, 18)
@@ -120,7 +117,7 @@
self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
self.SectionsComments = {}
- for section in SECTIONS_NAMES:
+ for section in self.Controler.SECTIONS_NAMES:
section_comment = " %s section " % (section)
len_headers = 78 - len(section_comment)
section_comment = self.COMMENT_HEADER * (len_headers / 2) + \
@@ -131,10 +128,11 @@
"comment": section_comment,
}
- for i, section in enumerate(SECTIONS_NAMES):
+ for i, section in enumerate(self.Controler.SECTIONS_NAMES):
section_infos = self.SectionsComments[section]
- if i + 1 < len(SECTIONS_NAMES):
- section_end = self.SectionsComments[SECTIONS_NAMES[i + 1]]["comment"]
+ if i + 1 < len(self.Controler.SECTIONS_NAMES):
+ section_end = self.SectionsComments[
+ self.Controler.SECTIONS_NAMES[i + 1]]["comment"]
else:
section_end = "$"
section_infos["pattern"] = re.compile(
@@ -203,7 +201,7 @@
def GetCodeText(self):
parts = self.Controler.GetTextParts()
text = ""
- for section in SECTIONS_NAMES:
+ for section in self.Controler.SECTIONS_NAMES:
section_comments = self.SectionsComments[section]
text += section_comments["comment"]
if parts[section] == "":
@@ -246,7 +244,7 @@
for line in xrange(self.GetLineCount()):
self.SetLineState(line, 0)
- for section in SECTIONS_NAMES:
+ for section in self.Controler.SECTIONS_NAMES:
section_comments = self.SectionsComments[section]
start_pos = text.find(section_comments["comment"])
end_pos = start_pos + len(section_comments["comment"])
@@ -263,7 +261,7 @@
def RefreshModel(self):
text = self.GetText()
parts = {}
- for section in SECTIONS_NAMES:
+ for section in self.Controler.SECTIONS_NAMES:
section_comments = self.SectionsComments[section]
result = section_comments["pattern"].search(text)
if result is not None:
@@ -276,14 +274,26 @@
if self.CallTipActive():
self.CallTipCancel()
key = event.GetKeyCode()
- current_pos = self.GetSelection()[0]
-
+ current_pos = self.GetCurrentPos()
+ selected = self.GetSelection()
+ text_selected = selected[0] != selected[1]
+
+ # Disable to type any character in section header lines
if (self.GetLineState(self.LineFromPosition(current_pos)) and
+ not text_selected and
key not in NAVIGATION_KEYS + [
wx.WXK_RETURN,
wx.WXK_NUMPAD_ENTER]):
return
+ # Disable to delete line between code and header lines
+ elif (self.GetCurLine()[0].strip() != "" and not text_selected and
+ (key == wx.WXK_BACK and
+ self.GetLineState(self.LineFromPosition(max(0, current_pos - 1))) or
+ key in [wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE] and
+ self.GetLineState(self.LineFromPosition(min(len(self.GetText()), current_pos + 1))))):
+ return
+
elif key == 32 and event.ControlDown():
pos = self.GetCurrentPos()
@@ -435,6 +445,7 @@
def Copy(self):
self.CmdKeyExecute(wx.stc.STC_CMD_COPY)
+ self.ParentWindow.RefreshEditMenu()
def Paste(self):
self.ResetBuffer()
--- a/py_ext/PythonFileCTNMixin.py Sun May 12 23:32:30 2013 +0200
+++ b/py_ext/PythonFileCTNMixin.py Mon May 13 14:31:23 2013 +0200
@@ -3,7 +3,7 @@
from PythonEditor import PythonEditor
from xml.dom import minidom
-from xmlclass import *
+from xmlclass import GenerateClassesFromXSD
import cPickle
from CodeFileTreeNode import CodeFile
@@ -12,6 +12,13 @@
class PythonFileCTNMixin(CodeFile):
+ CODEFILE_NAME = "PyFile"
+ SECTIONS_NAMES = [
+ "globals",
+ "init",
+ "cleanup",
+ "start",
+ "stop"]
EditorType = PythonEditor
def __init__(self):
@@ -39,53 +46,35 @@
def PythonFileName(self):
return os.path.join(self.CTNPath(), "py_ext.xml")
- def GetPythonCode(self):
- current_location = self.GetCurrentLocation()
- # define a unique name for the generated C file
- location_str = "_".join(map(str, current_location))
+ def GetSectionsCode(self):
- text = "## Code generated by Beremiz python mixin confnode\n\n"
-
- # Adding includes
- text += "## User includes\n"
- text += self.CodeFile.includes.gettext().strip()
- text += "\n"
-
- # Adding variables
- text += "## User variables reference\n"
+ # Generate Beremiz python runtime variables code
config = self.GetCTRoot().GetProjectConfigNames()[0]
+ variables_str = ""
for variable in self.CodeFile.variables.variable:
global_name = "%s_%s" % (config.upper(), variable.getname().upper())
- text += "# global_var:%s python_var:%s type:%s initial:%s\n" % (
+ variables_str += "# global_var:%s python_var:%s type:%s initial:%s\n" % (
global_name,
variable.getname(),
variable.gettype(),
str(variable.getinitial()))
- text += "\n"
- # Adding user global variables and routines
- text += "## User internal user variables and routines\n"
- text += self.CodeFile.globals.gettext().strip()
- text += "\n"
+ sections_code = {
+ "variables": variables_str,
+ "globals": self.CodeFile.globals.gettext().strip()
+ }
- # Adding Beremiz confnode functions
- text += "## Beremiz confnode functions\n"
- for func, args, return_code, code_object in [
- ("__init_", "*args, **kwargs",
- "return 0", self.CodeFile.initFunction),
- ("__cleanup_", "", "", self.CodeFile.cleanUpFunction),
- ("__retrieve_", "", "", self.CodeFile.retrieveFunction),
- ("__publish_", "", "", self.CodeFile.publishFunction),]:
- text += "def %s%s(%s):\n" % (func, location_str, args)
- lines = code_object.gettext().strip().splitlines()
- if len(lines) > 0 or return_code != "":
- for line in lines:
- text += " " + line + "\n"
- if return_code != "":
- text += " " + return_code + "\n"
- text += "\n"
- else:
- text += " pass\n\n"
-
- return text
+ # Generate Beremiz python runtime functions code
+ for section in self.SECTIONS_NAMES:
+ if section != "globals":
+ code_object = getattr(self.CodeFile, section)
+ section_str = ""
+ lines = code_object.gettext().strip().splitlines()
+ if len(lines) > 0:
+ for line in lines:
+ section_str += " " + line + "\n"
+ section_str += "\n"
+ sections_code[section] = section_str
+
+ return sections_code
--- a/py_ext/py_ext.py Sun May 12 23:32:30 2013 +0200
+++ b/py_ext/py_ext.py Mon May 13 14:31:23 2013 +0200
@@ -38,9 +38,35 @@
# define a unique name for the generated C file
location_str = "_".join(map(lambda x:str(x), current_location))
+ sections_code = self.GetSectionsCode()
+
+ text = "## Code generated by Beremiz python mixin confnode\n\n"
+
+ # Adding variables
+ text += "## User variables reference\n"
+ text += sections_code["variables"]
+ text += "\n"
+
+ # Adding user global variables and routines
+ text += "## User internal user variables and routines\n"
+ text += sections_code["globals"]
+ text += "\n"
+
+ # Adding Beremiz python runtime functions
+ text += "## Beremiz python runtime functions\n"
+ for section in self.SECTIONS_NAMES:
+ if section != "globals":
+ code_object = getattr(self.CodeFile, section)
+ text += "def _runtime_%s_%s():\n" % (location_str, section)
+ section_code = sections_code.get(section)
+ if section_code:
+ text += section_code
+ else:
+ text += " pass\n\n"
+
runtimefile_path = os.path.join(buildpath, "runtime_%s.py"%location_str)
runtimefile = open(runtimefile_path, 'w')
- runtimefile.write(self.GetPythonCode())
+ runtimefile.write(text)
runtimefile.close()
return [], "", False, ("runtime_%s.py"%location_str, file(runtimefile_path,"rb"))
--- a/wxglade_hmi/wxglade_hmi.py Sun May 12 23:32:30 2013 +0200
+++ b/wxglade_hmi/wxglade_hmi.py Mon May 13 14:31:23 2013 +0200
@@ -76,33 +76,59 @@
runtimefile.write(hmipyfile.read())
hmipyfile.close()
- runtimefile.write(self.GetPythonCode())
- runtimefile.write("""
-%(declare)s
-
-def _runtime_%(location)s_start():
- global %(global)s
-
- def OnCloseFrame(evt):
- wx.MessageBox(_("Please stop PLC to close"))
-
- %(init)s
-
-def _runtime_%(location)s_stop():
- global %(global)s
-
- %(cleanup)s
-
-""" % {"location": location_str,
- "declare": "\n".join(map(lambda x:"%s = None" % x, hmi_frames.keys())),
- "global": ",".join(hmi_frames.keys()),
- "init": "\n".join(map(lambda x: """
+ sections_code = self.GetSectionsCode()
+
+ # Adding variables
+ runtimefile.write("## User variables reference\n" +
+ sections_code["variables"] + "\n")
+
+ # Adding user global variables and routines
+ runtimefile.write("## User internal user variables and routines\n" +
+ sections_code["globals"] + "\n")
+
+ for section in ["init", "cleanup"]:
+ if not sections_code[section]:
+ sections_code = " pass"
+
+ sections_code.update({
+ "location": location_str,
+ "declare_hmi": "\n".join(map(lambda x:"%s = None" % x, hmi_frames.keys())),
+ "global_hmi": ",".join(hmi_frames.keys()),
+ "init_hmi": "\n".join(map(lambda x: """
%(name)s = %(class)s(None)
%(name)s.Bind(wx.EVT_CLOSE, OnCloseFrame)
%(name)s.Show()
""" % {"name": x[0], "class": x[1]},
hmi_frames.items())),
- "cleanup": "\n ".join(map(lambda x:"if %s is not None: %s.Destroy()" % (x,x), hmi_frames.keys()))})
+ "cleanup_hmi": "\n ".join(map(lambda x:"if %s is not None: %s.Destroy()" % (x,x), hmi_frames.keys()))})
+
+ runtimefile.write("""
+%(declare_hmi)s
+
+def _runtime_%(location)s_init():
+%(init)s
+
+def _runtime_%(location)s_cleanup():
+%(cleanup)s
+
+def _runtime_%(location)s_start():
+ global %(global_hmi)s
+
+%(start)s
+
+ def OnCloseFrame(evt):
+ wx.MessageBox(_("Please stop PLC to close"))
+
+ %(init_hmi)s
+
+def _runtime_%(location)s_stop():
+ global %(global_hmi)s
+
+ %(cleanup_hmi)s
+
+%(stop)s
+
+""" % sections_code)
runtimefile.close()
return [], "", False, ("runtime_%s.py"%location_str, file(runtimefile_path,"rb"))
--- a/xmlclass/__init__.py Sun May 12 23:32:30 2013 +0200
+++ b/xmlclass/__init__.py Mon May 13 14:31:23 2013 +0200
@@ -24,5 +24,5 @@
# Package initialisation
-from xmlclass import ClassFactory, GenerateClasses, GetAttributeValue, time_model, CreateNode, NodeSetAttr, NodeRenameAttr
+from xmlclass import ClassFactory, GenerateClasses, GetAttributeValue, time_model, CreateNode, NodeSetAttr, NodeRenameAttr, UpdateXMLClassGlobals
from xsdschema import XSDClassFactory, GenerateClassesFromXSD, GenerateClassesFromXSDstring
--- a/xmlclass/xmlclass.py Sun May 12 23:32:30 2013 +0200
+++ b/xmlclass/xmlclass.py Mon May 13 14:31:23 2013 +0200
@@ -1857,9 +1857,11 @@
def GenerateClasses(factory):
ComputedClasses = factory.CreateClasses()
if factory.FileName is not None and len(ComputedClasses) == 1:
- globals().update(ComputedClasses[factory.FileName])
+ UpdateXMLClassGlobals(ComputedClasses[factory.FileName])
return ComputedClasses[factory.FileName]
else:
- globals().update(ComputedClasses)
+ UpdateXMLClassGlobals(ComputedClasses)
return ComputedClasses
+def UpdateXMLClassGlobals(classes):
+ globals().update(classes)