andrej@1511: #!/usr/bin/env python andrej@1511: # -*- coding: utf-8 -*- andrej@1511: andrej@1511: # This file is part of Beremiz, a Integrated Development Environment for andrej@1511: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. andrej@1511: # andrej@1511: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD andrej@1511: # andrej@1511: # See COPYING file for copyrights details. andrej@1511: # andrej@1511: # This program is free software; you can redistribute it and/or andrej@1511: # modify it under the terms of the GNU General Public License andrej@1511: # as published by the Free Software Foundation; either version 2 andrej@1511: # of the License, or (at your option) any later version. andrej@1511: # andrej@1511: # This program is distributed in the hope that it will be useful, andrej@1511: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1511: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1511: # GNU General Public License for more details. andrej@1511: # andrej@1511: # You should have received a copy of the GNU General Public License andrej@1511: # along with this program; if not, write to the Free Software andrej@1511: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. andrej@1511: andrej@1511: andrej@1732: import os andrej@1732: import re andrej@1732: import traceback Laurent@1315: from copy import deepcopy andrej@2434: Laurent@1315: from lxml import etree Laurent@1315: from xmlclass import GenerateParserFromXSDstring laurent@401: Laurent@1096: from PLCControler import UndoBuffer Laurent@1330: from ConfigTreeNode import XSDSchemaErrorMessage laurent@630: Edouard@2527: from plcopen.plcopen import TestTextElement Edouard@2528: from editors.CodeFileEditor import GetSectionsText Edouard@2527: Laurent@1124: CODEFILE_XSD = """ Laurent@1315: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: %(includes_section)s Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Edouard@1448: Edouard@1448: Edouard@1448: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: %(sections)s Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Formatted text according to parts of XHTML 1.1 Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: Laurent@1124: """ Laurent@1124: Laurent@1124: SECTION_TAG_ELEMENT = "" lbessard@145: andrej@1736: andrej@1831: class CodeFile(object): andrej@1730: Laurent@1124: CODEFILE_NAME = "CodeFile" Laurent@1124: SECTIONS_NAMES = [] andrej@1730: etisserant@31: def __init__(self): Laurent@1124: sections_str = {"codefile_name": self.CODEFILE_NAME} Laurent@1124: if "includes" in self.SECTIONS_NAMES: Laurent@1124: sections_str["includes_section"] = SECTION_TAG_ELEMENT % "includes" Laurent@1124: else: Laurent@1124: sections_str["includes_section"] = "" Laurent@1124: sections_str["sections"] = "\n".join( Laurent@1124: [SECTION_TAG_ELEMENT % name Laurent@1124: for name in self.SECTIONS_NAMES if name != "includes"]) andrej@1730: Laurent@1315: self.CodeFileParser = GenerateParserFromXSDstring( Laurent@1124: CODEFILE_XSD % sections_str) Laurent@1315: self.CodeFileVariables = etree.XPath("variables/variable") andrej@1730: Laurent@1096: filepath = self.CodeFileName() lbessard@145: if os.path.isfile(filepath): edouard@3965: xmlfile = open(filepath, 'r', encoding='utf-8', errors='backslashreplace') Laurent@1315: codefile_xml = xmlfile.read() lbessard@145: xmlfile.close() andrej@1730: Laurent@1315: codefile_xml = codefile_xml.replace( andrej@1730: '<%s>' % self.CODEFILE_NAME, Laurent@1315: '<%s xmlns:xhtml="http://www.w3.org/1999/xhtml">' % self.CODEFILE_NAME) Laurent@1315: for cre, repl in [ andrej@2439: (re.compile(r"(?)(?:)(?!)"), "]]>")]: Laurent@1315: codefile_xml = cre.sub(repl, codefile_xml) andrej@1730: Laurent@1330: try: Laurent@1330: self.CodeFile, error = self.CodeFileParser.LoadXMLString(codefile_xml) Laurent@1330: if error is not None: andrej@1581: (fname, lnum, src) = ((self.CODEFILE_NAME,) + error) andrej@1744: self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1=fname, a2=lnum, a3=src)) Laurent@1330: self.CreateCodeFileBuffer(True) andrej@2418: except Exception as exc: kinsamanka@3752: msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1=self.CTNName(), a2=str(exc)) andrej@1581: self.GetCTRoot().logger.write_error(msg) Laurent@1330: self.GetCTRoot().logger.write_error(traceback.format_exc()) andrej@1872: raise Exception lbessard@145: else: Laurent@1315: self.CodeFile = self.CodeFileParser.CreateRoot() Laurent@1096: self.CreateCodeFileBuffer(False) Edouard@718: self.OnCTNSave() lbessard@145: lbessard@145: def GetBaseTypes(self): Edouard@718: return self.GetCTRoot().GetBaseTypes() lbessard@145: andrej@1744: def GetDataTypes(self, basetypes=False): Laurent@1096: return self.GetCTRoot().GetDataTypes(basetypes=basetypes) lbessard@145: surkovsv93@1969: def GenerateNewName(self, name, format): Laurent@1146: return self.GetCTRoot().GenerateNewName( surkovsv93@1969: None, name, format, surkovsv93@1969: exclude=dict([(var.getname().upper(), True) surkovsv93@1969: for var in self.CodeFile.variables.getvariable()])) Laurent@1146: lbessard@145: def SetVariables(self, variables): Laurent@1096: self.CodeFile.variables.setvariable([]) lbessard@145: for var in variables: Laurent@1315: variable = self.CodeFileParser.CreateElement("variable", "variables") lbessard@145: variable.setname(var["Name"]) lbessard@145: variable.settype(var["Type"]) Laurent@1096: variable.setinitial(var["Initial"]) Edouard@1448: variable.setdesc(var["Description"]) Edouard@1448: variable.setonchange(var["OnChange"]) Edouard@1448: variable.setopts(var["Options"]) Laurent@1096: self.CodeFile.variables.appendvariable(variable) andrej@1730: lbessard@145: def GetVariables(self): lbessard@145: datas = [] Laurent@1315: for var in self.CodeFileVariables(self.CodeFile): andrej@1773: datas.append({ andrej@1773: "Name": var.getname(), andrej@1773: "Type": var.gettype(), andrej@1773: "Initial": var.getinitial(), andrej@1773: "Description": var.getdesc(), andrej@1773: "OnChange": var.getonchange(), andrej@1773: "Options": var.getopts(), andrej@1773: }) lbessard@145: return datas lbessard@145: Laurent@1096: def SetTextParts(self, parts): Laurent@1124: for section in self.SECTIONS_NAMES: Laurent@1124: section_code = parts.get(section) Laurent@1124: if section_code is not None: Laurent@1315: getattr(self.CodeFile, section).setanyText(section_code) andrej@1730: Laurent@1096: def GetTextParts(self): Laurent@1315: return dict([(section, getattr(self.CodeFile, section).getanyText()) Laurent@1124: for section in self.SECTIONS_NAMES]) andrej@1730: Edouard@718: def CTNTestModified(self): andrej@1730: return self.ChangesToSave or not self.CodeFileIsSaved() laurent@630: Laurent@1061: def OnCTNSave(self, from_project_path=None): Laurent@1096: filepath = self.CodeFileName() andrej@1730: andrej@1740: xmlfile = open(filepath, "w") Laurent@1315: xmlfile.write(etree.tostring( andrej@1730: self.CodeFile, andrej@1730: pretty_print=True, andrej@1730: xml_declaration=True, kinsamanka@3783: encoding='utf-8').decode()) lbessard@145: xmlfile.close() andrej@1730: Laurent@1096: self.MarkCodeFileAsSaved() etisserant@31: return True etisserant@31: Laurent@1095: def CTNGlobalInstances(self): Edouard@1448: variables = self.CodeFileVariables(self.CodeFile) andrej@1758: ret = [(variable.getname(), andrej@1758: variable.gettype(), andrej@1758: variable.getinitial()) andrej@1767: for variable in variables] Edouard@1448: return ret laurent@656: Edouard@2523: def CTNSearch(self, criteria): Edouard@2527: variables = self.GetVariables() Edouard@2527: results = [] Edouard@2527: tagname = self.CTNFullName() Edouard@2527: for index, var in enumerate(variables): Edouard@2527: varname = var["Name"] Edouard@2527: results.extend([((tagname, "var_inout", index, "name"),) + result Edouard@2527: for result in TestTextElement(varname, criteria)]) Edouard@2528: results.extend([((tagname, "body"),) + result Edouard@2528: for result in TestTextElement( Edouard@2528: GetSectionsText(self, lambda x:""), criteria)]) Edouard@2528: return results Edouard@2523: andrej@1782: # ------------------------------------------------------------------------------- lbessard@145: # Current Buffering Management Functions andrej@1782: # ------------------------------------------------------------------------------- lbessard@145: lbessard@145: def Copy(self, model): andrej@1837: """ andrej@1837: Return a copy of the codefile model andrej@1837: """ Laurent@1315: return deepcopy(model) lbessard@145: Laurent@1096: def CreateCodeFileBuffer(self, saved): laurent@658: self.Buffering = False Laurent@1315: self.CodeFileBuffer = UndoBuffer(self.CodeFileParser.Dumps(self.CodeFile), saved) laurent@651: Laurent@1096: def BufferCodeFile(self): Laurent@1315: self.CodeFileBuffer.Buffering(self.CodeFileParser.Dumps(self.CodeFile)) andrej@1730: lbessard@145: def StartBuffering(self): lbessard@145: self.Buffering = True andrej@1730: lbessard@145: def EndBuffering(self): lbessard@145: if self.Buffering: Laurent@1315: self.CodeFileBuffer.Buffering(self.CodeFileParser.Dumps(self.CodeFile)) lbessard@145: self.Buffering = False andrej@1730: Laurent@1096: def MarkCodeFileAsSaved(self): laurent@651: self.EndBuffering() Laurent@1096: self.CodeFileBuffer.CurrentSaved() andrej@1730: Laurent@1096: def CodeFileIsSaved(self): Laurent@1096: return self.CodeFileBuffer.IsCurrentSaved() and not self.Buffering andrej@1730: lbessard@145: def LoadPrevious(self): laurent@651: self.EndBuffering() Laurent@1315: self.CodeFile = self.CodeFileParser.Loads(self.CodeFileBuffer.Previous()) andrej@1730: lbessard@145: def LoadNext(self): Laurent@1315: self.CodeFile = self.CodeFileParser.Loads(self.CodeFileBuffer.Next()) andrej@1730: lbessard@145: def GetBufferState(self): Laurent@1096: first = self.CodeFileBuffer.IsFirst() and not self.Buffering Laurent@1096: last = self.CodeFileBuffer.IsLast() lbessard@145: return not first, not last