--- a/CodeFileTreeNode.py Fri Sep 27 09:32:39 2013 +0900
+++ b/CodeFileTreeNode.py Sat Sep 28 15:59:50 2013 +0900
@@ -1,10 +1,11 @@
+import os, re, traceback from copy import deepcopy
from xmlclass import GenerateParserFromXSDstring
from PLCControler import UndoBuffer
+from ConfigTreeNode import XSDSchemaErrorMessage CODEFILE_XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
@@ -84,9 +85,16 @@
(re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["),
(re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]:
codefile_xml = cre.sub(repl, codefile_xml)
- self.CodeFile = etree.fromstring(codefile_xml, self.CodeFileParser)
- self.CreateCodeFileBuffer(True)
+ self.CodeFile, error = self.CodeFileParser.LoadXMLString(codefile_xml) + self.GetCTRoot().logger.write_warning( + XSDSchemaErrorMessage % ((self.CODEFILE_NAME,) + error)) + self.CreateCodeFileBuffer(True) + self.GetCTRoot().logger.write_error(_("Couldn't load confnode parameters %s :\n %s") % (CTNName, unicode(exc))) + self.GetCTRoot().logger.write_error(traceback.format_exc()) self.CodeFile = self.CodeFileParser.CreateRoot()
self.CreateCodeFileBuffer(False)
--- a/ConfigTreeNode.py Fri Sep 27 09:32:39 2013 +0900
+++ b/ConfigTreeNode.py Sat Sep 28 15:59:50 2013 +0900
@@ -29,6 +29,7 @@
+XSDSchemaErrorMessage = _("%s XML file doesn't follow XSD schema at line %d:\n%s") @@ -581,29 +582,36 @@
if os.path.isfile(methode_name):
+ ConfNodeName = CTNName if CTNName is not None else self.CTNName() basexmlfile = open(self.ConfNodeBaseXmlFilePath(CTNName), 'r')
- self.BaseParams = etree.fromstring(
- basexmlfile.read(), _BaseParamsParser)
+ self.BaseParams, error = _BaseParamsParser.LoadXMLString(basexmlfile.read()) + self.GetCTRoot().logger.write_warning( + XSDSchemaErrorMessage % ((ConfNodeName + " BaseParams",) + error)) self.MandatoryParams = ("BaseParams", self.BaseParams)
- self.GetCTRoot().logger.write_error(_("Couldn't load confnode base parameters %s :\n %s") % (CTNName, unicode(exc)))
+ self.GetCTRoot().logger.write_error(_("Couldn't load confnode base parameters %s :\n %s") % (ConfNodeName, unicode(exc))) self.GetCTRoot().logger.write_error(traceback.format_exc())
xmlfile = open(self.ConfNodeXmlFilePath(CTNName), 'r')
- obj = etree.fromstring(xmlfile.read(), self.Parser)
+ obj, error = self.Parser.LoadXMLString(xmlfile.read()) + self.GetCTRoot().logger.write_warning( + XSDSchemaErrorMessage % ((ConfNodeName,) + error)) self.CTNParams = (name, obj)
- self.GetCTRoot().logger.write_error(_("Couldn't load confnode parameters %s :\n %s") % (CTNName, unicode(exc)))
+ self.GetCTRoot().logger.write_error(_("Couldn't load confnode parameters %s :\n %s") % (ConfNodeName, unicode(exc))) self.GetCTRoot().logger.write_error(traceback.format_exc())
--- a/PLCControler.py Fri Sep 27 09:32:39 2013 +0900
+++ b/PLCControler.py Sat Sep 28 15:59:50 2013 +0900
@@ -844,8 +844,10 @@
Adds the POU defined by 'pou_xml' to the current project with type 'pou_type'
- new_pou = LoadPou(pou_xml)
+ new_pou, error = LoadPou(pou_xml) return _("Couldn't paste non-POU object.")
@@ -2207,10 +2209,10 @@
- instances = LoadPouInstances(text.encode("utf-8"), bodytype)
- if len(instances) == 0:
+ instances, error = LoadPouInstances(text.encode("utf-8"), bodytype) + instances, error = [], "" + if error is not None or len(instances) == 0: return _("Invalid plcopen element(s)!!!")
@@ -2285,24 +2287,25 @@
instance.translate(*diff)
return new_id, connections
- # Return the current pou editing informations
- def GetEditedElementInstanceInfos(self, tagname, id = None, exclude = [], debug = False):
+ # Return the current pou editing instances idx + def GetEditedElementInstancesIds(self, tagname, debug = False): element = self.GetEditedElement(tagname, debug)
- instance = element.getinstance(id)
- instance = element.getrandomInstance(exclude)
- if instance is not None:
- infos = instance.getinfos()
- if infos["type"] in ["input", "output", "inout"]:
- var_type = self.GetEditedElementVarValueType(tagname, infos["specific_values"]["name"], debug)
- infos["specific_values"]["value_type"] = var_type
+ return element.getinstancesIds() + # Return the current pou editing informations + def GetEditedElementInstanceInfos(self, tagname, id, debug = False): + element = self.GetEditedElement(tagname, debug) + if element is not None: + instance = element.getinstance(id) + if instance is not None: + infos = instance.getinfos() + if infos["type"] in ["input", "output", "inout"]: + var_type = self.GetEditedElementVarValueType(tagname, infos["specific_values"]["name"], debug) + infos["specific_values"]["value_type"] = var_type def ClearEditedElementExecutionOrder(self, tagname):
@@ -3063,10 +3066,9 @@
return tasks_data, instances_data
def OpenXMLFile(self, filepath):
- self.Project = LoadProject(filepath)
- # return _("Project file syntax error:\n\n") + str(e)
+ self.Project, error = LoadProject(filepath) + if self.Project is None: + return _("Project file syntax error:\n\n") + error self.SetFilePath(filepath)
self.CreateProjectBuffer(True)
@@ -3075,7 +3077,7 @@
self.CurrentCompiledProject = None
self.CurrentElementEditing = None
def SaveXMLFile(self, filepath = None):
if not filepath and self.FilePath == "":
--- a/PLCOpenEditor.py Fri Sep 27 09:32:39 2013 +0900
+++ b/PLCOpenEditor.py Sat Sep 28 15:59:50 2013 +0900
@@ -182,12 +182,11 @@
# Create a new controller
controler = PLCControler()
result = controler.OpenXMLFile(fileOpen)
- self.Controler = controler
- self.LibraryPanel.SetController(controler)
- self.ProjectTree.Enable(True)
- self.PouInstanceVariablesPanel.SetController(controler)
- self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
+ self.Controler = controler + self.LibraryPanel.SetController(controler) + self.ProjectTree.Enable(True) + self.PouInstanceVariablesPanel.SetController(controler) + self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) # Define PLCOpenEditor icon
self.SetIcon(wx.Icon(os.path.join(CWD, "images", "poe.ico"),wx.BITMAP_TYPE_ICO))
@@ -197,7 +196,8 @@
self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
- self.ShowErrorMessage(result)
+ _("PLC syntax error at line %d:\n%s") % result) def OnCloseFrame(self, event):
if self.Controler is None or self.CheckSaveBeforeClosing(_("Close Application")):
@@ -300,17 +300,17 @@
controler = PLCControler()
result = controler.OpenXMLFile(filepath)
- self.Controler = controler
- self.LibraryPanel.SetController(controler)
- self.ProjectTree.Enable(True)
- self.PouInstanceVariablesPanel.SetController(controler)
- self._Refresh(PROJECTTREE, LIBRARYTREE)
+ self.Controler = controler + self.LibraryPanel.SetController(controler) + self.ProjectTree.Enable(True) + self.PouInstanceVariablesPanel.SetController(controler) + self._Refresh(PROJECTTREE, LIBRARYTREE) self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
- self.ShowErrorMessage(result)
+ _("PLC syntax error at line %d:\n%s") % result) def OnCloseProjectMenu(self, event):
if not self.CheckSaveBeforeClosing():
--- a/ProjectController.py Fri Sep 27 09:32:39 2013 +0900
+++ b/ProjectController.py Sat Sep 28 15:59:50 2013 +0900
@@ -27,7 +27,7 @@
from PLCControler import PLCControler
from plcopen.structures import IEC_KEYWORDS
from targets.typemapping import DebugTypesSize, LogLevelsCount, LogLevels
-from ConfigTreeNode import ConfigTreeNode
+from ConfigTreeNode import ConfigTreeNode, XSDSchemaErrorMessage base_folder = os.path.split(sys.path[0])[0]
@@ -318,9 +318,13 @@
if not os.path.isfile(plc_file):
return _("Chosen folder doesn't contain a program. It's not a valid project!")
- result = self.OpenXMLFile(plc_file)
+ error = self.OpenXMLFile(plc_file) + if self.Project is not None: + self.logger.write_warning( + XSDSchemaErrorMessage % (("PLC",) + error)) if len(self.GetProjectConfigNames()) == 0:
self.AddProjectDefaultConfiguration()
# Change XSD into class members
--- a/editors/Viewer.py Fri Sep 27 09:32:39 2013 +0900
+++ b/editors/Viewer.py Sat Sep 28 15:59:50 2013 +0900
@@ -1089,10 +1089,11 @@
# List of ids of already loaded blocks
+ ids = self.Controler.GetEditedElementInstancesIds(self.TagName, debug = self.Debug) # Load Blocks until they are all loaded
- while instance is not None:
- instance = self.Controler.GetEditedElementInstanceInfos(self.TagName, exclude = ids, debug = self.Debug)
+ instance = self.Controler.GetEditedElementInstanceInfos( + self.TagName, ids.popitem(0)[0], debug = self.Debug) self.loadInstance(instance, ids, selection)
@@ -1221,7 +1222,6 @@
# Load instance from given informations
def loadInstance(self, instance, ids, selection):
- ids.append(instance["id"])
self.current_id = max(self.current_id, instance["id"])
creation_function = ElementCreationFunctions.get(instance["type"], None)
connectors = {"inputs" : [], "outputs" : []}
@@ -1319,7 +1319,7 @@
- if refLocalId not in ids:
+ if ids.pop(refLocalId, False): new_instance = self.Controler.GetEditedElementInstanceInfos(self.TagName, refLocalId, debug = self.Debug)
if new_instance is not None:
self.loadInstance(new_instance, ids, selection)
--- a/plcopen/plcopen.py Fri Sep 27 09:32:39 2013 +0900
+++ b/plcopen/plcopen.py Sat Sep 28 15:59:50 2013 +0900
@@ -26,6 +26,8 @@
+from collections import OrderedDict Dictionary that makes the relation between var names in plcopen and displayed values
@@ -156,35 +158,44 @@
-def LoadProject(filepath):
- project_file = open(filepath)
- project_xml = project_file.read().replace(
+def LoadProjectXML(project_xml): + project_xml = project_xml.replace( "http://www.plcopen.org/xml/tc6.xsd",
"http://www.plcopen.org/xml/tc6_0201")
(re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["),
(re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]:
project_xml = cre.sub(repl, project_xml)
+ tree, error = PLCOpenParser.LoadXMLString(project_xml) + # TODO Validate file according to PLCOpen v1 and modify it for + # compatibility with PLCOpen v2 +def LoadProject(filepath): + project_file = open(filepath) + project_xml = project_file.read()
- return etree.fromstring(project_xml, PLCOpenParser)
+ return LoadProjectXML(project_xml) project_pou_xpath = PLCOpen_XPath("/ppx:project/ppx:types/ppx:pous/ppx:pou")
- root = etree.fromstring(
- LOAD_POU_PROJECT_TEMPLATE % xml_string,
- return project_pou_xpath(root)[0]
+ root, error = LoadProjectXML(LOAD_POU_PROJECT_TEMPLATE % xml_string) + return project_pou_xpath(root)[0], error project_pou_instances_xpath = {
body_type: PLCOpen_XPath(
"/ppx:project/ppx:types/ppx:pous/ppx:pou[@name='paste_pou']/ppx:body/ppx:%s/*" % body_type)
for body_type in ["FBD", "LD", "SFC"]}
def LoadPouInstances(xml_string, body_type):
- root = etree.fromstring(
- LOAD_POU_INSTANCES_PROJECT_TEMPLATE(body_type) % xml_string,
- return project_pou_instances_xpath[body_type](root)
+ root, error = LoadProjectXML( + LOAD_POU_INSTANCES_PROJECT_TEMPLATE(body_type) % xml_string) + return project_pou_instances_xpath[body_type](root), error def SaveProject(project, filepath):
project_file = open(filepath, 'w')
@@ -1080,11 +1091,11 @@
setattr(cls, "getinstance", getinstance)
- def getrandomInstance(self, exclude):
+ def getinstancesIds(self): - return self.body[0].getcontentRandomInstance(exclude)
- setattr(cls, "getrandomInstance", getrandomInstance)
+ return self.body[0].getcontentInstancesIds() + setattr(cls, "getinstancesIds", getinstancesIds) def getinstanceByName(self, name):
@@ -1600,18 +1611,13 @@
raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag()
setattr(cls, "getcontentInstance", getcontentInstance)
- def getcontentRandomInstance(self, exclude):
+ def getcontentInstancesIds(self): if self.content.getLocalTag() in ["LD","FBD","SFC"]:
- instance = self.content.xpath("*%s[position()=1]" %
- ("[not(%s)]" % " or ".join(
- map(lambda x: "@localId=%d" % x, exclude))
- if len(exclude) > 0 else ""))
+ return OrderedDict([(instance.getlocalId(), True) + for instance in self.content]) raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag()
- setattr(cls, "getcontentRandomInstance", getcontentRandomInstance)
+ setattr(cls, "getcontentInstancesIds", getcontentInstancesIds) def getcontentInstanceByName(self, name):
if self.content.getLocalTag() in ["LD","FBD","SFC"]:
--- a/plcopen/structures.py Fri Sep 27 09:32:39 2013 +0900
+++ b/plcopen/structures.py Sat Sep 28 15:59:50 2013 +0900
@@ -42,8 +42,10 @@
ScriptDirectory = os.path.split(os.path.realpath(__file__))[0]
-StdBlockLibrary = LoadProject(os.path.join(ScriptDirectory, "Standard_Function_Blocks.xml"))
-AddnlBlockLibrary = LoadProject(os.path.join(ScriptDirectory, "Additional_Function_Blocks.xml"))
+StdBlockLibrary, error = LoadProject( + os.path.join(ScriptDirectory, "Standard_Function_Blocks.xml")) +AddnlBlockLibrary, error = LoadProject( + os.path.join(ScriptDirectory, "Additional_Function_Blocks.xml")) "SR": _("SR bistable\nThe SR bistable is a latch where the Set dominates."),
--- a/py_ext/PythonFileCTNMixin.py Fri Sep 27 09:32:39 2013 +0900
+++ b/py_ext/PythonFileCTNMixin.py Sat Sep 28 15:59:50 2013 +0900
@@ -37,12 +37,20 @@
(re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["),
(re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]:
pythonfile_xml = cre.sub(repl, pythonfile_xml)
- python_code = etree.fromstring(pythonfile_xml, PythonParser)
- self.CodeFile.globals.setanyText(python_code.getanyText())
- self.CreateCodeFileBuffer(False)
+ python_code, error = PythonParser.LoadXMLString(pythonfile_xml) + self.CodeFile.globals.setanyText(python_code.getanyText()) + self.CreateCodeFileBuffer(False) + self.GetCTRoot().logger.write_error( + _("Couldn't import old %s file.") % CTNName) return os.path.join(self.CTNPath(), "pyfile.xml")
--- a/xmlclass/xmlclass.py Fri Sep 27 09:32:39 2013 +0900
+++ b/xmlclass/xmlclass.py Sat Sep 28 15:59:50 2013 +0900
@@ -1730,7 +1730,7 @@
class XMLClassParser(etree.XMLParser):
- def __init__(self, namespaces, default_namespace_format, base_class, *args, **kwargs):
+ def __init__(self, namespaces, default_namespace_format, base_class, xsd_schema, *args, **kwargs): etree.XMLParser.__init__(self, *args, **kwargs)
self.DefaultNamespaceFormat = default_namespace_format
@@ -1742,11 +1742,19 @@
self.RootNSMAP = namespaces
self.BaseClass = base_class
+ self.XSDSchema = xsd_schema def set_element_class_lookup(self, class_lookup):
etree.XMLParser.set_element_class_lookup(self, class_lookup)
self.ClassLookup = class_lookup
+ def LoadXMLString(self, xml_string): + tree = etree.fromstring(xml_string, self) + if not self.XSDSchema.validate(tree): + error = self.XSDSchema.error_log.last_error + return tree, (error.line, error.message) def Dumps(self, xml_obj):
return etree.tostring(xml_obj)
@@ -1793,7 +1801,7 @@
factory.etreeNamespaceFormat,
BaseClass[0] if len(BaseClass) == 1 else None,
- schema = etree.XMLSchema(etree.fromstring(xsdstring)),
+ etree.XMLSchema(etree.fromstring(xsdstring)), strip_cdata = False, remove_blank_text=True)
class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp)
parser.set_element_class_lookup(class_lookup)