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