Exemple #1
0
class ModbusSlaveFile(CodeFile):

    CODEFILE_NAME = "mk200mbSlave"
    SECTIONS_NAMES = [ "includes", "globals", "initFunction", "cleanUpFunction", "retrieveFunction", "publishFunction"]

    EditorType = ModbusFileEditor

    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.CodeFileParser = GenerateParserFromXSDstring(
            CODEFILE_XSD % sections_str)
        self.CodeFileVariables = etree.XPath("variables/variable")

        filepath = self.CodeFileName()

        if os.path.isfile(filepath):
            xmlfile = open(filepath, 'r')
            codefile_xml = xmlfile.read()
            xmlfile.close()

            codefile_xml = codefile_xml.replace(
                '<%s>' % self.CODEFILE_NAME,
                '<%s xmlns:xhtml="http://www.w3.org/1999/xhtml">' % self.CODEFILE_NAME)
            for cre, repl in [
                (re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["),
                (re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]:
                codefile_xml = cre.sub(repl, codefile_xml)

            try:
                self.CodeFile, error = self.CodeFileParser.LoadXMLString(codefile_xml)
                if error is not None:
                    self.GetCTRoot().logger.write_warning(
                        XSDSchemaErrorMessage % ((self.CODEFILE_NAME,) + error))
                self.CreateCodeFileBuffer(True)
            except Exception, exc:
                self.GetCTRoot().logger.write_error(_("Couldn't load confnode parameters %s :\n %s") % (CTNName, unicode(exc)))
                self.GetCTRoot().logger.write_error(traceback.format_exc())
        else:
Exemple #2
0
class ConfigTreeNode(object):
    """
    This class is the one that define confnodes.
    """

    XSD = None
    CTNChildrenTypes = []
    CTNMaxCount = None
    ConfNodeMethods = []
    LibraryControler = None
    EditorType = ConfTreeNodeEditor
    IconPath = None

    def _AddParamsMembers(self):
        self.CTNParams = None
        if self.XSD:
            self.Parser = GenerateParserFromXSDstring(self.XSD)
            obj = self.Parser.CreateRoot()
            name = obj.getLocalTag()
            self.CTNParams = (name, obj)
            setattr(self, name, obj)

    def __init__(self):
        # Create BaseParam
        self.BaseParams = _BaseParamsParser.CreateRoot()
        self.MandatoryParams = ("BaseParams", self.BaseParams)
        self._AddParamsMembers()
        self.Children = {}
        self._View = None
        # copy ConfNodeMethods so that it can be later customized
        self.ConfNodeMethods = [dic.copy() for dic in self.ConfNodeMethods]

    def ConfNodeBaseXmlFilePath(self, CTNName=None):
        return os.path.join(self.CTNPath(CTNName), "baseconfnode.xml")

    def ConfNodeXmlFilePath(self, CTNName=None):
        return os.path.join(self.CTNPath(CTNName), "confnode.xml")

    def ConfNodePath(self):
        return os.path.join(self.CTNParent.ConfNodePath(), self.CTNType)

    def CTNPath(self, CTNName=None, project_path=None):
        if not CTNName:
            CTNName = self.CTNName()
        if not project_path:
            project_path = self.CTNParent.CTNPath()
        return os.path.join(project_path,
                            CTNName + NameTypeSeparator + self.CTNType)

    def CTNName(self):
        return self.BaseParams.getName()

    def CTNEnabled(self):
        return self.BaseParams.getEnabled()

    def CTNFullName(self):
        parent = self.CTNParent.CTNFullName()
        if parent != "":
            return parent + "." + self.CTNName()
        return self.BaseParams.getName()

    def CTNSearch(self, criteria):
        # TODO match config's fields name and fields contents
        return reduce(add, [
            CTNChild.CTNSearch(criteria)
            for CTNChild in self.IterChildren()], [])

    def GetIconName(self):
        return None

    def CTNTestModified(self):
        return self.ChangesToSave

    def ProjectTestModified(self):
        """
        recursively check modified status
        """
        if self.CTNTestModified():
            return True

        for CTNChild in self.IterChildren():
            if CTNChild.ProjectTestModified():
                return True

        return False

    def RemoteExec(self, script, **kwargs):
        return self.CTNParent.RemoteExec(script, **kwargs)

    def OnCTNSave(self, from_project_path=None):
        """Default, do nothing and return success"""
        return True

    def GetParamsAttributes(self, path=None):
        if path:
            parts = path.split(".", 1)
            if self.MandatoryParams and parts[0] == self.MandatoryParams[0]:
                return self.MandatoryParams[1].getElementInfos(parts[0], parts[1])
            elif self.CTNParams and parts[0] == self.CTNParams[0]:
                return self.CTNParams[1].getElementInfos(parts[0], parts[1])
        else:
            params = []
            if self.CTNParams:
                params.append(self.CTNParams[1].getElementInfos(self.CTNParams[0]))
            return params

    def SetParamsAttribute(self, path, value):
        self.ChangesToSave = True
        # Filter IEC_Channel and Name, that have specific behavior
        if path == "BaseParams.IEC_Channel":
            old_leading = ".".join(map(str, self.GetCurrentLocation()))
            new_value = self.FindNewIEC_Channel(value)
            if new_value != value:
                new_leading = ".".join(map(str, self.CTNParent.GetCurrentLocation() + (new_value,)))
                self.GetCTRoot().UpdateProjectVariableLocation(old_leading, new_leading)
            return new_value, True
        elif path == "BaseParams.Name":
            res = self.FindNewName(value)
            self.CTNRequestSave()
            return res, True

        parts = path.split(".", 1)
        if self.MandatoryParams and parts[0] == self.MandatoryParams[0]:
            self.MandatoryParams[1].setElementValue(parts[1], value)
            value = self.MandatoryParams[1].getElementInfos(parts[0], parts[1])["value"]
        elif self.CTNParams and parts[0] == self.CTNParams[0]:
            self.CTNParams[1].setElementValue(parts[1], value)
            value = self.CTNParams[1].getElementInfos(parts[0], parts[1])["value"]
        return value, False

    def CTNMakeDir(self):
        os.mkdir(self.CTNPath())

    def CTNRequestSave(self, from_project_path=None):
        if self.GetCTRoot().CheckProjectPathPerm(False):
            # If confnode do not have corresponding directory
            ctnpath = self.CTNPath()
            if not os.path.isdir(ctnpath):
                # Create it
                os.mkdir(ctnpath)

            # generate XML for base XML parameters controller of the confnode
            if self.MandatoryParams:
                BaseXMLFile = open(self.ConfNodeBaseXmlFilePath(), 'w', encoding='utf-8')
                BaseXMLFile.write(etree.tostring(
                    self.MandatoryParams[1],
                    pretty_print=True,
                    xml_declaration=True,
                    encoding='utf-8').decode())
                BaseXMLFile.close()

            # generate XML for XML parameters controller of the confnode
            if self.CTNParams:
                XMLFile = open(self.ConfNodeXmlFilePath(), 'w', encoding='utf-8')
                XMLFile.write(etree.tostring(
                    self.CTNParams[1],
                    pretty_print=True,
                    xml_declaration=True,
                    encoding='utf-8').decode())
                XMLFile.close()

            # Call the confnode specific OnCTNSave method
            result = self.OnCTNSave(from_project_path)
            if not result:
                return _("Error while saving \"%s\"\n") % self.CTNPath()

            # mark confnode as saved
            self.ChangesToSave = False
            # go through all children and do the same
            for CTNChild in self.IterChildren():
                CTNChildPath = None
                if from_project_path is not None:
                    CTNChildPath = CTNChild.CTNPath(project_path=from_project_path)
                result = CTNChild.CTNRequestSave(CTNChildPath)
                if result:
                    return result
        return None

    def CTNImport(self, src_CTNPath):
        shutil.copytree(src_CTNPath, self.CTNPath)
        return True

    def CTNGlobalInstances(self):
        """
        @return: [(instance_name, instance_type),...]
        """
        return []

    def _GlobalInstances(self):
        instances = self.CTNGlobalInstances()
        for CTNChild in self.IECSortedChildren():
            instances.extend(CTNChild._GlobalInstances())
        return instances

    def CTNGenerate_C(self, buildpath, locations):
        """
        Generate C code
        @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
        """
        self.GetCTRoot().logger.write_warning(".".join(map(str, self.GetCurrentLocation())) + " -> Nothing to do\n")
        return [], "", False, []

    def _Generate_C(self, buildpath, locations):
        # Generate confnodes [(Cfiles, CFLAGS)], LDFLAGS, DoCalls, extra_files
        # extra_files = [(fname,fobject), ...]
        gen_result = self.CTNGenerate_C(buildpath, locations)
        CTNCFilesAndCFLAGS, CTNLDFLAGS, DoCalls, libs = gen_result[:4]
        extra_files = gen_result[4:]
        # if some files have been generated put them in the list with their location
        if CTNCFilesAndCFLAGS:
            LocationCFilesAndCFLAGS = [(self.GetCurrentLocation(), CTNCFilesAndCFLAGS, DoCalls)]
        else:
            LocationCFilesAndCFLAGS = []

        # confnode asks for some LDFLAGS
        LDFLAGS = []
        if CTNLDFLAGS is not None:
            # LDFLAGS can be either string
            if isinstance(CTNLDFLAGS, str):
                LDFLAGS += [CTNLDFLAGS]
            # or list of strings
            elif isinstance(CTNLDFLAGS, list):
                LDFLAGS += CTNLDFLAGS

        # recurse through all children, and stack their results
        for CTNChild in self.IECSortedChildren():
            new_location = CTNChild.GetCurrentLocation()
            # How deep are we in the tree ?
            depth = len(new_location)
            _LocationCFilesAndCFLAGS, _LDFLAGS, _lib, _extra_files = \
                CTNChild._Generate_C(
                    # keep the same path
                    buildpath,
                    # filter locations that start with current IEC location
                    [loc for loc in locations if loc["LOC"][0:depth] == new_location])
            # stack the result
            LocationCFilesAndCFLAGS += _LocationCFilesAndCFLAGS
            LDFLAGS += _LDFLAGS
            extra_files += _extra_files
            libs += _lib
            libs = list(set(libs))
        return LocationCFilesAndCFLAGS, LDFLAGS, libs, extra_files

    def IterChildren(self):
        for _CTNType, Children in list(self.Children.items()):
            for CTNInstance in Children:
                yield CTNInstance

    def IECSortedChildren(self):
        # reorder children by IEC_channels
        ordered = [(chld.BaseParams.getIEC_Channel(), chld) for chld in self.IterChildren()]
        if ordered:
            ordered = sorted(ordered)
            return list(zip(*ordered))[1]
        else:
            return []

    def _GetChildBySomething(self, something, toks):
        for CTNInstance in self.IterChildren():
            # if match component of the name
            if getattr(CTNInstance.BaseParams, something) == toks[0]:
                # if Name have other components
                if len(toks) >= 2:
                    # Recurse in order to find the latest object
                    return CTNInstance._GetChildBySomething(something, toks[1:])
                # No sub name -> found
                return CTNInstance
        # Not found
        return None

    def GetChildByName(self, Name):
        '''child节点的BaseParams需要有Name属性'''
        if Name:
            toks = Name.split('.')
            return self._GetChildBySomething("Name", toks)
        else:
            return self

    def GetChildByIECLocation(self, Location):
        if Location:
            return self._GetChildBySomething("IEC_Channel", Location)
        else:
            return self

    def GetCurrentLocation(self):
        """
        @return:  Tupple containing confnode IEC location of current confnode : %I0.0.4.5 => (0,0,4,5)
        """
        return self.CTNParent.GetCurrentLocation() + (self.BaseParams.getIEC_Channel(),)

    def GetCurrentName(self):
        """
        @return:  String "ParentParentName.ParentName.Name"
        """
        return self.CTNParent._GetCurrentName() + self.BaseParams.getName()

    def _GetCurrentName(self):
        """
        @return:  String "ParentParentName.ParentName.Name."
        """
        return self.CTNParent._GetCurrentName() + self.BaseParams.getName() + "."

    def GetCTRoot(self):
        return self.CTNParent.GetCTRoot()

    def GetFullIEC_Channel(self):
        return ".".join([str(i) for i in self.GetCurrentLocation()]) + ".x"

    def GetLocations(self):
        location = self.GetCurrentLocation()
        return [loc for loc in self.CTNParent.GetLocations() if loc["LOC"][0:len(location)] == location]

    def GetVariableLocationTree(self):
        '''
        This function is meant to be overridden by confnodes.

        It should returns an list of dictionaries

        - IEC_type is an IEC type like BOOL/BYTE/SINT/...
        - location is a string of this variable's location, like "%IX0.0.0"
        '''
        children = []
        for child in self.IECSortedChildren():
            children.append(child.GetVariableLocationTree())
        return {"name": self.BaseParams.getName(),
                "type": LOCATION_CONFNODE,
                "location": self.GetFullIEC_Channel(),
                "children": children}

    def FindNewName(self, DesiredName):
        """
        Changes Name to DesiredName if available, Name-N if not.
        @param DesiredName: The desired Name (string)
        """

        # Build a list of used Name out of parent's Children
        AllNames = []
        for CTNInstance in self.CTNParent.IterChildren():
            if CTNInstance != self:
                AllNames.append(CTNInstance.BaseParams.getName())

        # Find a free name, eventually appending digit
        res = DesiredName
        if DesiredName.endswith("_0"):
            BaseDesiredName = DesiredName[:-2]
        else:
            BaseDesiredName = DesiredName
        suffix = 1
        while res in AllNames:
            res = "%s_%d" % (BaseDesiredName, suffix)
            suffix += 1

        oldname = self.CTNPath()
        # Check previous confnode existance
        dontexist = self.BaseParams.getName() == "__unnamed__"
            # Get old path
        # Set the new name
        self.BaseParams.setName(res)
        # Rename confnode dir if exist
        if not dontexist:
            shutil.move(oldname, self.CTNPath())
        # warn user he has two left hands
        if DesiredName != res:
            msg = _("A child named \"{a1}\" already exists -> \"{a2}\"\n").format(a1=DesiredName, a2=res)
            self.GetCTRoot().logger.write_warning(msg)
        return res

    def GetAllChannels(self):
        AllChannels = []
        for CTNInstance in self.CTNParent.IterChildren():
            if CTNInstance != self:
                AllChannels.append(CTNInstance.BaseParams.getIEC_Channel())
        AllChannels = sorted(AllChannels)
        return AllChannels

    def FindNewIEC_Channel(self, DesiredChannel):
        """
        Changes IEC Channel number to DesiredChannel if available, nearest available if not.
        @param DesiredChannel: The desired IEC channel (int)
        """
        # Get Current IEC channel
        CurrentChannel = self.BaseParams.getIEC_Channel()
        # Do nothing if no change
        # if CurrentChannel == DesiredChannel: return CurrentChannel
        # Build a list of used Channels out of parent's Children
        AllChannels = self.GetAllChannels()

        # Now, try to guess the nearest available channel
        res = DesiredChannel
        while res in AllChannels:  # While channel not free
            if res < CurrentChannel:  # Want to go down ?
                res -= 1  # Test for n-1
                if res < 0:
                    self.GetCTRoot().logger.write_warning(
                        _("Cannot find lower free IEC channel than %d\n") % CurrentChannel)
                    return CurrentChannel  # Can't go bellow 0, do nothing
            else:  # Want to go up ?
                res += 1  # Test for n-1
        # Finally set IEC Channel
        self.BaseParams.setIEC_Channel(res)
        return res

    def GetContextualMenuItems(self):
        return None

    def GetView(self):
        if self._View is None and self.EditorType is not None:
            app_frame = self.GetCTRoot().AppFrame
            win = wx.MDIChildFrame(app_frame, -1, self.CTNType)
            self._View = self.EditorType(win, self, app_frame)
            win.Bind(wx.EVT_CLOSE, self.onDestroy)
            win.Maximize()
            win.Show(True)
        return self._View

    def onDestroy(self, event):
        self._View = None
        event.Skip()

    def _OpenView(self, name=None, onlyopened=False):
        view = self.GetView()

        if view is not None:
            if name is None:
                name = self.CTNFullName()
            app_frame = self.GetCTRoot().AppFrame
            app_frame.EditProjectElement(view, name)
        view.RefreshView()
        return view

    def _CloseView(self, view):
        app_frame = self.GetCTRoot().AppFrame
        if app_frame is not None:
            app_frame.DeletePage(view)

    def OnCloseEditor(self, view):
        if self._View == view:
            self._View = None

    def OnCTNClose(self):
        if self._View is not None:
            self._CloseView(self._View)
            self._View = None
        return True

    def _doRemoveChild(self, CTNInstance):
        # Remove all children of child
        for SubCTNInstance in CTNInstance.IterChildren():
            CTNInstance._doRemoveChild(SubCTNInstance)
        # Call the OnCloseMethod
        CTNInstance.OnCTNClose()
        # Delete confnode dir
        try:
            shutil.rmtree(CTNInstance.CTNPath())
        except Exception:
            pass
        # Remove child of Children
        self.Children[CTNInstance.CTNType].remove(CTNInstance)
        if len(self.Children[CTNInstance.CTNType]) == 0:
            self.Children.pop(CTNInstance.CTNType)
        # Forget it... (View have to refresh)

    def CTNRemove(self):
        # Fetch the confnode
        # CTNInstance = self.GetChildByName(CTNName)
        # Ask to his parent to remove it
        self.OnCTNClose()
        self.CTNParent._doRemoveChild(self)

    def CTNAddChild(self, CTNName, CTNType, IEC_Channel=0):
        """
        Create the confnodes that may be added as child to this node self
        @param CTNType: string desining the confnode class name (get name from CTNChildrenTypes)
        @param CTNName: string for the name of the confnode instance
        """
        # reorganize self.CTNChildrenTypes tuples from (name, CTNClass, Help)
        # to ( name, (CTNClass, Help)), an make a dict
        transpose = list(zip(*self.CTNChildrenTypes))
        CTNChildrenTypes = dict(list(zip(transpose[0], list(zip(transpose[1], transpose[2])))))
        # Check that adding this confnode is allowed
        try:
            CTNClass, CTNHelp = CTNChildrenTypes[CTNType]
        except KeyError:
            raise Exception(_("Cannot create child {a1} of type {a2} ").
                            format(a1=CTNName, a2=CTNType))

        # if CTNClass is a class factory, call it. (prevent unneeded imports)
        if isinstance(CTNClass, types.FunctionType):
            CTNClass = CTNClass()

        # Eventualy Initialize child instance list for this class of confnode
        ChildrenWithSameClass = self.Children.setdefault(CTNType, list())
        # Check count
        if getattr(CTNClass, "CTNMaxCount", None) and len(ChildrenWithSameClass) >= CTNClass.CTNMaxCount:
            raise Exception(
                _("Max count ({a1}) reached for this confnode of type {a2} ").
                    format(a1=CTNClass.CTNMaxCount, a2=CTNType))

        # create the final class, derived of provided confnode and template
        class FinalCTNClass(CTNClass, ConfigTreeNode):
            """
            ConfNode class is derivated into FinalCTNClass before being instanciated
            This way __init__ is overloaded to ensure ConfigTreeNode.__init__ is called
            before CTNClass.__init__, and to do the file related stuff.
            """

            def __init__(self, parent):
                self.CTNParent = parent
                # Keep track of the confnode type name
                self.CTNType = CTNType
                # remind the help string, for more fancy display
                self.CTNHelp = CTNHelp
                # Call the base confnode template init - change XSD into class members
                ConfigTreeNode.__init__(self)
                # check name is unique
                NewCTNName = self.FindNewName(CTNName)
                # If dir have already be made, and file exist
                if os.path.isdir(self.CTNPath(NewCTNName)):  # and os.path.isfile(self.ConfNodeXmlFilePath(CTNName)):
                    # Load the confnode.xml file into parameters members
                    self.LoadXMLParams(NewCTNName)
                    # Basic check. Better to fail immediately.
                    if self.BaseParams.getName() != NewCTNName:
                        raise Exception(
                            _("Project tree layout do not match confnode.xml {a1}!={a2} ").
                                format(a1=NewCTNName, a2=self.BaseParams.getName()))

                    # Now, self.CTNPath() should be OK

                    # Check that IEC_Channel is not already in use.
                    self.FindNewIEC_Channel(self.BaseParams.getIEC_Channel())
                    # Call the confnode real __init__
                    if getattr(CTNClass, "__init__", None):
                        CTNClass.__init__(self)
                    # Load and init all the children
                    self.LoadChildren()
                    # just loaded, nothing to saved
                    self.ChangesToSave = False
                else:
                    # If confnode do not have corresponding file/dirs - they will be created on Save
                    self.CTNMakeDir()
                    # Find an IEC number
                    self.FindNewIEC_Channel(IEC_Channel)
                    # Call the confnode real __init__
                    if getattr(CTNClass, "__init__", None):
                        CTNClass.__init__(self)
                    self.CTNRequestSave()
                    # just created, must be saved
                    self.ChangesToSave = True

            def _getBuildPath(self):
                return self.CTNParent._getBuildPath()

        # Create the object out of the resulting class
        newConfNodeOpj = FinalCTNClass(self)
        # Store it in CTNgedChils
        ChildrenWithSameClass.append(newConfNodeOpj)

        return newConfNodeOpj

    def ClearChildren(self):
        for child in self.IterChildren():
            child.ClearChildren()
        self.Children = {}

    def LoadXMLParams(self, CTNName=None):
        methode_name = os.path.join(self.CTNPath(CTNName), "methods.py")
        if os.path.isfile(methode_name):
            exec(open(methode_name).read())

        ConfNodeName = CTNName if CTNName is not None else self.CTNName()

        # Get the base xml tree
        if self.MandatoryParams:
            try:
                basexmlfile = open(self.ConfNodeBaseXmlFilePath(CTNName), 'r')
                self.BaseParams, error = _BaseParamsParser.LoadXMLString(basexmlfile.read())
                if error is not None:
                    (fname, lnum, src) = ((ConfNodeName + " BaseParams",) + error)
                    self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1=fname, a2=lnum, a3=src))
                self.MandatoryParams = ("BaseParams", self.BaseParams)
                basexmlfile.close()
            except Exception as exc:
                msg = _("Couldn't load confnode base parameters {a1} :\n {a2}").format(a1=ConfNodeName, a2=str(exc))
                self.GetCTRoot().logger.write_error(msg)
                print(traceback.format_exc())

        # Get the xml tree
        if self.CTNParams:
            try:
                xmlfile = open(self.ConfNodeXmlFilePath(CTNName), 'r', encoding='utf-8')
                obj, error = self.Parser.LoadXMLString(xmlfile.read())
                if error is not None:
                    (fname, lnum, src) = ((ConfNodeName,) + error)
                    self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1=fname, a2=lnum, a3=src))
                name = obj.getLocalTag()
                setattr(self, name, obj)
                self.CTNParams = (name, obj)
                xmlfile.close()
            except Exception as exc:
                msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1=ConfNodeName, a2=str(exc))
                self.GetCTRoot().logger.write_error(msg)
                print(traceback.format_exc())

    def LoadChildren(self):
        # Iterate over all CTNName@CTNType in confnode directory, and try to open them
        for CTNDir in os.listdir(self.CTNPath()):
            if os.path.isdir(os.path.join(self.CTNPath(), CTNDir)) and \
                    CTNDir.count(NameTypeSeparator) == 1:
                pname, ptype = CTNDir.split(NameTypeSeparator)
                try:
                    self.CTNAddChild(pname, ptype)
                except Exception as exc:
                    msg = _("Could not add child \"{a1}\", type {a2} :\n{a3}\n").format(a1=pname, a2=ptype,
                                                                                        a3=str(exc))
                    self.GetCTRoot().logger.write_error(msg)
                    print(traceback.format_exc())
class CodeFile(object):
    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.CodeFileParser = GenerateParserFromXSDstring(
            CODEFILE_XSD % sections_str)
        self.CodeFileVariables = etree.XPath("variables/variable")

        filepath = self.CodeFileName()
        if os.path.isfile(filepath):
            xmlfile = open(filepath, 'r', encoding='utf-8')
            codefile_xml = xmlfile.read()
            xmlfile.close()

            codefile_xml = codefile_xml.replace(
                '<%s>' % self.CODEFILE_NAME,
                '<%s xmlns:xhtml="http://www.w3.org/1999/xhtml">' % self.CODEFILE_NAME)
            for cre, repl in [
                (re.compile(r"(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["),
                (re.compile(r"(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]:
                codefile_xml = cre.sub(repl, codefile_xml)

            try:
                self.CodeFile, error = self.CodeFileParser.LoadXMLString(codefile_xml)
                if error is not None:
                    (fname, lnum, src) = ((self.CODEFILE_NAME,) + error)
                    self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1=fname, a2=lnum, a3=src))
                self.CreateCodeFileBuffer(True)
            except Exception as exc:
                msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1=self.CTNName(), a2=str(exc))
                self.GetCTRoot().logger.write_error(msg)
                print(traceback.format_exc())
                raise Exception
        else:
            self.CodeFile = self.CodeFileParser.CreateRoot()
            self.CreateCodeFileBuffer(False)
            self.OnCTNSave()

    def GetBaseTypes(self):
        return self.GetCTRoot().GetBaseTypes()

    def GetDataTypes(self, basetypes=False):
        return self.GetCTRoot().GetDataTypes(basetypes=basetypes)

    def GenerateNewName(self, name, format):
        return self.GetCTRoot().GenerateNewName(
            None, name, format,
            exclude=dict([(var.getname().upper(), True)
                          for var in self.CodeFile.variables.getvariable()]))

    def SetVariables(self, variables):
        self.CodeFile.variables.setvariable([])
        for var in variables:
            variable = self.CodeFileParser.CreateElement("variable", "variables")
            variable.setname(var["Name"])
            variable.settype(var["Type"])
            variable.setinitial(var["Initial"])
            variable.setdesc(var["Description"])
            variable.setonchange(var["OnChange"])
            variable.setopts(var["Options"])
            self.CodeFile.variables.appendvariable(variable)

    def GetVariables(self):
        datas = []
        for var in self.CodeFileVariables(self.CodeFile):
            datas.append({
                "Name": var.getname(),
                "Type": var.gettype(),
                "Initial": var.getinitial(),
                "Description": var.getdesc(),
                "OnChange": var.getonchange(),
                "Options": var.getopts(),
            })
        return datas

    def SetTextParts(self, parts):
        for section in self.SECTIONS_NAMES:
            section_code = parts.get(section)
            if section_code is not None:
                getattr(self.CodeFile, section).setanyText(section_code)

    def GetTextParts(self):
        return dict([(section, getattr(self.CodeFile, section).getanyText())
                     for section in self.SECTIONS_NAMES])

    def CTNTestModified(self):
        return self.ChangesToSave or not self.CodeFileIsSaved()

    def OnCTNSave(self, from_project_path=None):
        filepath = self.CodeFileName()

        xmlfile = open(filepath, "w", encoding='utf-8')
        xmlfile.write(etree.tostring(
            self.CodeFile,
            pretty_print=True,
            xml_declaration=True,
            encoding='utf-8').decode())
        xmlfile.close()

        self.MarkCodeFileAsSaved()
        return True

    def CTNGlobalInstances(self):
        variables = self.CodeFileVariables(self.CodeFile)
        ret = [(variable.getname(),
                variable.gettype(),
                variable.getinitial())
               for variable in variables]
        ret.extend([("On" + variable.getname() + "Change", "python_poll", "")
                    for variable in variables
                    if variable.getonchange()])
        return ret

    def CTNSearch(self, criteria):
        variables = self.GetVariables()
        results = []
        tagname = self.CTNFullName()
        for index, var in enumerate(variables):
            varname = var["Name"]
            results.extend([((tagname, "var_inout", index, "name"),) + result
                            for result in TestTextElement(varname, criteria)])
        results.extend([((tagname, "body"),) + result
                        for result in TestTextElement(
                GetSectionsText(self, lambda x: ""), criteria)])
        return results
    # -------------------------------------------------------------------------------
    #                      Current Buffering Management Functions
    # -------------------------------------------------------------------------------

    def Copy(self, model):
        """
        Return a copy of the codefile model
        """
        return deepcopy(model)

    def CreateCodeFileBuffer(self, saved):
        self.Buffering = False
        self.CodeFileBuffer = UndoBuffer(self.CodeFileParser.Dumps(self.CodeFile), saved)

    def BufferCodeFile(self):
        self.CodeFileBuffer.Buffering(self.CodeFileParser.Dumps(self.CodeFile))

    def StartBuffering(self):
        self.Buffering = True

    def EndBuffering(self):
        if self.Buffering:
            self.CodeFileBuffer.Buffering(self.CodeFileParser.Dumps(self.CodeFile))
            self.Buffering = False

    def MarkCodeFileAsSaved(self):
        self.EndBuffering()
        self.CodeFileBuffer.CurrentSaved()

    def CodeFileIsSaved(self):
        return self.CodeFileBuffer.IsCurrentSaved() and not self.Buffering

    def LoadPrevious(self):
        self.EndBuffering()
        self.CodeFile = self.CodeFileParser.Loads(self.CodeFileBuffer.Previous())

    def LoadNext(self):
        self.CodeFile = self.CodeFileParser.Loads(self.CodeFileBuffer.Next())

    def GetBufferState(self):
        first = self.CodeFileBuffer.IsFirst() and not self.Buffering
        last = self.CodeFileBuffer.IsLast()
        return not first, not last