etherlab/etherlab.py
changeset 2111 f2cffda17d00
parent 2109 86832a0e113d
child 2133 ba0b2ca7db26
equal deleted inserted replaced
2110:e8c43f542eb1 2111:f2cffda17d00
     1 import os, shutil
     1 import os, shutil
     2 import cPickle
       
     3 from xml.dom import minidom
     2 from xml.dom import minidom
     4 
     3 
     5 import wx
     4 import wx
     6 import csv
     5 import csv
     7 
     6 
     8 from xmlclass import *
     7 from xmlclass import *
     9 from POULibrary import POULibrary
     8 
    10 from ConfigTreeNode import ConfigTreeNode
     9 from ConfigTreeNode import ConfigTreeNode
    11 from PLCControler import UndoBuffer, LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
    10 from PLCControler import UndoBuffer, LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
    12 from ConfigEditor import NodeEditor, CIA402NodeEditor, MasterEditor, LibraryEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE
    11 
    13 from dialogs import BrowseValuesLibraryDialog
    12 from EthercatSlave import ExtractHexDecValue, ExtractName
    14 from IDEFrame import TITLE, FILEMENU, PROJECTTREE
    13 from EthercatMaster import _EthercatCTN
    15 
    14 from ConfigEditor import LibraryEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE
    16 try:
       
    17     from MotionLibrary import Headers, AxisXSD
       
    18     HAS_MCL = True
       
    19 except:
       
    20     HAS_MCL = False
       
    21 
       
    22 TYPECONVERSION = {"BOOL" : "X", "SINT" : "B", "INT" : "W", "DINT" : "D", "LINT" : "L",
       
    23     "USINT" : "B", "UINT" : "W", "UDINT" : "D", "ULINT" : "L", 
       
    24     "BYTE" : "B", "WORD" : "W", "DWORD" : "D", "LWORD" : "L"}
       
    25 
       
    26 DATATYPECONVERSION = {"BOOL" : "BIT", "SINT" : "S8", "INT" : "S16", "DINT" : "S32", "LINT" : "S64",
       
    27     "USINT" : "U8", "UINT" : "U16", "UDINT" : "U32", "ULINT" : "U64", 
       
    28     "BYTE" : "U8", "WORD" : "U16", "DWORD" : "U32", "LWORD" : "U64"}
       
    29 
       
    30 VARCLASSCONVERSION = {"T": LOCATION_VAR_INPUT, "R": LOCATION_VAR_OUTPUT, "RT": LOCATION_VAR_MEMORY}
       
    31 
       
    32 #--------------------------------------------------
       
    33 #         Remote Exec Etherlab Commands
       
    34 #--------------------------------------------------
       
    35 
       
    36 SCAN_COMMAND = """
       
    37 import commands
       
    38 result = commands.getoutput("ethercat slaves")
       
    39 slaves = []
       
    40 for slave_line in result.splitlines():
       
    41     chunks = slave_line.split()
       
    42     idx, pos, state, flag = chunks[:4]
       
    43     name = " ".join(chunks[4:])
       
    44     alias, position = pos.split(":")
       
    45     slave = {"idx": int(idx),
       
    46              "alias": int(alias),
       
    47              "position": int(position),
       
    48              "name": name}
       
    49     details = commands.getoutput("ethercat slaves -p %d -v" % slave["idx"])
       
    50     for details_line in details.splitlines():
       
    51         details_line = details_line.strip()
       
    52         for header, param in [("Vendor Id:", "vendor_id"),
       
    53                               ("Product code:", "product_code"),
       
    54                               ("Revision number:", "revision_number")]:
       
    55             if details_line.startswith(header):
       
    56                 slave[param] = details_line.split()[-1]
       
    57                 break
       
    58     slaves.append(slave)
       
    59 returnVal = slaves
       
    60 """
       
    61 
       
    62 #--------------------------------------------------
       
    63 #      Etherlab Specific Blocks Library
       
    64 #--------------------------------------------------
       
    65 
       
    66 def GetLocalPath(filename):
       
    67     return os.path.join(os.path.split(__file__)[0], filename)
       
    68 
       
    69 class EtherlabLibrary(POULibrary):
       
    70     def GetLibraryPath(self):
       
    71         return GetLocalPath("pous.xml")
       
    72 
       
    73     def Generate_C(self, buildpath, varlist, IECCFLAGS):
       
    74         etherlab_ext_file = open(GetLocalPath("etherlab_ext.c"), 'r')
       
    75         etherlab_ext_code = etherlab_ext_file.read()
       
    76         etherlab_ext_file.close()
       
    77         
       
    78         Gen_etherlabfile_path = os.path.join(buildpath, "etherlab_ext.c")
       
    79         ethelabfile = open(Gen_etherlabfile_path,'w')
       
    80         ethelabfile.write(etherlab_ext_code)
       
    81         ethelabfile.close()
       
    82         
       
    83         runtimefile_path = os.path.join(os.path.split(__file__)[0], "runtime_etherlab.py")
       
    84         return ((["etherlab_ext"], [(Gen_etherlabfile_path, IECCFLAGS)], True), "", 
       
    85                 ("runtime_etherlab.py", file(GetLocalPath("runtime_etherlab.py"))))
       
    86 
       
    87 #--------------------------------------------------
       
    88 #                    Ethercat Node
       
    89 #--------------------------------------------------
       
    90 
       
    91 class _EthercatSlaveCTN:
       
    92     
       
    93     NODE_PROFILE = None
       
    94     EditorType = NodeEditor
       
    95     
       
    96     def GetIconName(self):
       
    97         return "Slave"
       
    98     
       
    99     def ExtractHexDecValue(self, value):
       
   100         return ExtractHexDecValue(value)
       
   101     
       
   102     def GetSizeOfType(self, type):
       
   103         return TYPECONVERSION.get(self.GetCTRoot().GetBaseType(type), None)
       
   104     
       
   105     def GetSlavePos(self):
       
   106         return self.BaseParams.getIEC_Channel()
       
   107     
       
   108     def GetParamsAttributes(self, path = None):
       
   109         if path:
       
   110             parts = path.split(".", 1)
       
   111             if self.MandatoryParams and parts[0] == self.MandatoryParams[0]:
       
   112                 return self.MandatoryParams[1].getElementInfos(parts[0], parts[1])
       
   113             elif self.CTNParams and parts[0] == self.CTNParams[0]:
       
   114                 return self.CTNParams[1].getElementInfos(parts[0], parts[1])
       
   115         else:
       
   116             params = []
       
   117             if self.CTNParams:
       
   118                 params.append(self.CTNParams[1].getElementInfos(self.CTNParams[0]))
       
   119             else:
       
   120                 params.append({
       
   121                     'use': 'required', 
       
   122                     'type': 'element', 
       
   123                     'name': 'SlaveParams', 
       
   124                     'value': None, 
       
   125                     'children': []
       
   126                 })
       
   127             
       
   128             slave_type = self.CTNParent.GetSlaveType(self.GetSlavePos())
       
   129             params[0]['children'].insert(0,
       
   130                    {'use': 'optional', 
       
   131                     'type': self.CTNParent.GetSlaveTypesLibrary(self.NODE_PROFILE), 
       
   132                     'name': 'Type', 
       
   133                     'value': (slave_type["device_type"], slave_type)}) 
       
   134             params[0]['children'].insert(1,
       
   135                    {'use': 'optional', 
       
   136                     'type': 'unsignedLong', 
       
   137                     'name': 'Alias', 
       
   138                     'value': self.CTNParent.GetSlaveAlias(self.GetSlavePos())})
       
   139             return params
       
   140         
       
   141     def SetParamsAttribute(self, path, value):
       
   142         position = self.BaseParams.getIEC_Channel()
       
   143         
       
   144         if path == "SlaveParams.Type":
       
   145             self.CTNParent.SetSlaveType(position, value)
       
   146             slave_type = self.CTNParent.GetSlaveType(self.GetSlavePos())
       
   147             value = (slave_type["device_type"], slave_type)
       
   148             if self._View is not None:
       
   149                 wx.CallAfter(self._View.RefreshSlaveInfos)
       
   150             return value, True
       
   151         elif path == "SlaveParams.Alias":
       
   152             self.CTNParent.SetSlaveAlias(position, value)
       
   153             return value, True
       
   154         
       
   155         value, refresh = ConfigTreeNode.SetParamsAttribute(self, path, value)
       
   156         
       
   157         # Filter IEC_Channel, Slave_Type and Alias that have specific behavior
       
   158         if path == "BaseParams.IEC_Channel" and value != position:
       
   159             self.CTNParent.SetSlavePosition(position, value)
       
   160         
       
   161         return value, refresh
       
   162         
       
   163     def GetSlaveInfos(self):
       
   164         return self.CTNParent.GetSlaveInfos(self.GetSlavePos())
       
   165     
       
   166     def GetSlaveVariables(self, limits):
       
   167         return self.CTNParent.GetSlaveVariables(self.GetSlavePos(), limits)
       
   168     
       
   169     def GetVariableLocationTree(self):
       
   170         return  {"name": self.BaseParams.getName(),
       
   171                  "type": LOCATION_CONFNODE,
       
   172                  "location": self.GetFullIEC_Channel(),
       
   173                  "children": self.CTNParent.GetDeviceLocationTree(self.GetSlavePos(), self.GetCurrentLocation(), self.BaseParams.getName())
       
   174         }
       
   175 
       
   176     def CTNGenerate_C(self, buildpath, locations):
       
   177         return [],"",False
       
   178 
       
   179 #--------------------------------------------------
       
   180 #                 Ethercat CIA402 Node
       
   181 #--------------------------------------------------
       
   182 
       
   183 if HAS_MCL:
       
   184     
       
   185     NODE_VARIABLES = [
       
   186         ("ControlWord", 0x6040, 0x00, "UINT", "Q"),
       
   187         ("TargetPosition", 0x607a, 0x00, "DINT", "Q"),
       
   188         ("ModesOfOperation", 0x06060, 0x00, "SINT", "Q"),
       
   189         ("StatusWord", 0x6041, 0x00, "UINT", "I"),
       
   190         ("ModesOfOperationDisplay", 0x06061, 0x00, "SINT", "I"),
       
   191         ("ActualPosition", 0x6064, 0x00, "DINT", "I"),
       
   192         ("ActualVelocity", 0x606C, 0x00, "DINT", "I"),
       
   193     ]
       
   194     
       
   195     DEFAULT_RETRIEVE = "    __CIA402Node_%(location)s.axis->%(name)s = *(__CIA402Node_%(location)s.%(name)s);"
       
   196     DEFAULT_PUBLISH = "    *(__CIA402Node_%(location)s.%(name)s) = __CIA402Node_%(location)s.axis->%(name)s;"
       
   197     
       
   198     EXTRA_NODE_VARIABLES = [
       
   199         ("ErrorCode", [
       
   200             {"description": ("ErrorCode", 0x603F, 0x00, "UINT", "I"),
       
   201              "publish": None}
       
   202             ]),
       
   203         ("DigitalInputs", [
       
   204             {"description": ("DigitalInputs", 0x60FD, 0x00, "UDINT", "I"),
       
   205              "publish": None}
       
   206             ]),
       
   207         ("DigitalOutputs", [
       
   208             {"description": ("DigitalOutputs", 0x60FE, 0x00, "UDINT", "Q"),
       
   209              "retrieve": None}
       
   210             ])
       
   211     ]
       
   212     EXTRA_NODE_VARIABLES_DICT = dict([("Enable" + name, value) for name, value in EXTRA_NODE_VARIABLES])
       
   213 
       
   214     BLOCK_INPUT_TEMPLATE = "    __SET_VAR(%(blockname)s->,%(input_name)s, %(input_value)s);"
       
   215     BLOCK_OUTPUT_TEMPLATE = "    __SET_VAR(data__->,%(output_name)s, __GET_VAR(%(blockname)s->%(output_name)s));"
       
   216     
       
   217     BLOCK_FUNCTION_TEMPLATE = """
       
   218 extern void ETHERLAB%(ucase_blocktype)s_body__(ETHERLAB%(ucase_blocktype)s* data__);
       
   219 void __%(blocktype)s_%(location)s(MC_%(ucase_blocktype)s *data__) {
       
   220     __DECLARE_GLOBAL_PROTOTYPE(ETHERLAB%(ucase_blocktype)s, %(blockname)s);
       
   221     ETHERLAB%(ucase_blocktype)s* %(blockname)s = __GET_GLOBAL_%(blockname)s();
       
   222 %(extract_inputs)s
       
   223     ETHERLAB%(ucase_blocktype)s_body__(%(blockname)s);
       
   224 %(return_outputs)s
       
   225 }
       
   226 """
       
   227     
       
   228     BLOCK_FUNTION_DEFINITION_TEMPLATE = """    if (!__CIA402Node_%(location)s.axis->__mcl_func_MC_%(blocktype)s)
       
   229     __CIA402Node_%(location)s.axis->__mcl_func_MC_%(blocktype)s = __%(blocktype)s_%(location)s;"""
       
   230     
       
   231     GLOBAL_INSTANCES = [
       
   232         {"blocktype": "GetTorqueLimit", 
       
   233          "inputs": [],
       
   234          "outputs": [{"name": "TorqueLimitPos", "type": "UINT"},
       
   235                      {"name": "TorqueLimitNeg", "type": "UINT"}]},
       
   236         {"blocktype": "SetTorqueLimit", 
       
   237          "inputs": [{"name": "TorqueLimitPos", "type": "UINT"},
       
   238                     {"name": "TorqueLimitNeg", "type": "UINT"}],
       
   239          "outputs": []},
       
   240     ]
       
   241     
       
   242     class _EthercatCIA402SlaveCTN(_EthercatSlaveCTN):
       
   243         XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
       
   244         <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       
   245           <xsd:element name="CIA402SlaveParams">
       
   246             <xsd:complexType>
       
   247               %s
       
   248             </xsd:complexType>
       
   249           </xsd:element>
       
   250         </xsd:schema>
       
   251         """ % ("\n".join(['<xsd:attribute name="Enable%s" type="xsd:boolean" use="optional" default="false"/>' % category 
       
   252                           for category, variables in EXTRA_NODE_VARIABLES]) + AxisXSD)
       
   253         
       
   254         NODE_PROFILE = 402
       
   255         EditorType = CIA402NodeEditor
       
   256         
       
   257         ConfNodeMethods = [
       
   258             {"bitmap" : "CIA402AxisRef",
       
   259              "name" : _("Axis Ref"),
       
   260              "tooltip" : _("Initiate Drag'n drop of Axis ref located variable"),
       
   261              "method" : "_getCIA402AxisRef",
       
   262              "push": True},
       
   263         ]
       
   264         
       
   265         def GetIconName(self):
       
   266             return "CIA402Slave"
       
   267         
       
   268         def SetParamsAttribute(self, path, value):
       
   269             if path == "CIA402SlaveParams.Type":
       
   270                 path = "SlaveParams.Type"
       
   271             elif path == "CIA402SlaveParams.Alias":
       
   272                 path = "SlaveParams.Alias"
       
   273             return _EthercatSlaveCTN.SetParamsAttribute(self, path, value)
       
   274         
       
   275         def GetVariableLocationTree(self):
       
   276             axis_name = self.CTNName()
       
   277             current_location = self.GetCurrentLocation()
       
   278             children = [{"name": "%s Axis Ref" % (axis_name),
       
   279                          "type": LOCATION_VAR_INPUT,
       
   280                          "size": "W",
       
   281                          "IEC_type": "AXIS_REF",
       
   282                          "var_name": axis_name,
       
   283                          "location": "%%IW%s.0" % (".".join(map(str, current_location))),
       
   284                          "description": "",
       
   285                          "children": []}]
       
   286             children.extend(self.CTNParent.GetDeviceLocationTree(self.GetSlavePos(), current_location, axis_name))
       
   287             return  {"name": axis_name,
       
   288                      "type": LOCATION_CONFNODE,
       
   289                      "location": self.GetFullIEC_Channel(),
       
   290                      "children": children,
       
   291             }
       
   292         
       
   293         def CTNGlobalInstances(self):
       
   294             current_location = self.GetCurrentLocation()
       
   295             return [("%s_%s" % (block_infos["blocktype"], "_".join(map(str, current_location))),
       
   296                      "EtherLab%s" % block_infos["blocktype"]) for block_infos in GLOBAL_INSTANCES]
       
   297         
       
   298         def _getCIA402AxisRef(self):
       
   299             data = wx.TextDataObject(str(("%%IW%s.0" % ".".join(map(str, self.GetCurrentLocation())), 
       
   300                                           "location", "AXIS_REF", self.CTNName(), "")))
       
   301             dragSource = wx.DropSource(self.GetCTRoot().AppFrame)
       
   302             dragSource.SetData(data)
       
   303             dragSource.DoDragDrop()
       
   304         
       
   305         def CTNGenerate_C(self, buildpath, locations):
       
   306             current_location = self.GetCurrentLocation()
       
   307             
       
   308             location_str = "_".join(map(lambda x:str(x), current_location))
       
   309             
       
   310             plc_cia402node_filepath = os.path.join(os.path.split(__file__)[0], "plc_cia402node.c")
       
   311             plc_cia402node_file = open(plc_cia402node_filepath, 'r')
       
   312             plc_cia402node_code = plc_cia402node_file.read()
       
   313             plc_cia402node_file.close()
       
   314             
       
   315             str_completion = {
       
   316                 "slave_pos": self.GetSlavePos(),
       
   317                 "location": location_str,
       
   318                 "MCL_headers": Headers,
       
   319                 "extern_located_variables_declaration": [],
       
   320                 "fieldbus_interface_declaration": [],
       
   321                 "fieldbus_interface_definition": [],
       
   322                 "entry_variables": [],
       
   323                 "init_axis_params": [],
       
   324                 "init_entry_variables": [],
       
   325                 "extra_variables_retrieve": [],
       
   326                 "extra_variables_publish": []
       
   327             }
       
   328             
       
   329             for blocktype_infos in GLOBAL_INSTANCES:
       
   330                 texts = {
       
   331                     "blocktype": blocktype_infos["blocktype"],
       
   332                     "ucase_blocktype": blocktype_infos["blocktype"].upper(),
       
   333                     "location": "_".join(map(str, current_location))
       
   334                 }
       
   335                 texts["blockname"] = "%(ucase_blocktype)s_%(location)s" % texts
       
   336                 
       
   337                 inputs = [{"input_name": "POS", "input_value": str(self.GetSlavePos())},
       
   338                           {"input_name": "EXECUTE", "input_value": "__GET_VAR(data__->EXECUTE)"}] +\
       
   339                          [{"input_name": input["name"].upper(), 
       
   340                            "input_value": "__GET_VAR(data__->%s)" % input["name"].upper()}
       
   341                           for input in blocktype_infos["inputs"]]
       
   342                 input_texts = []
       
   343                 for input_infos in inputs:
       
   344                     input_infos.update(texts)
       
   345                     input_texts.append(BLOCK_INPUT_TEMPLATE % input_infos)
       
   346                 texts["extract_inputs"] = "\n".join(input_texts)
       
   347                 
       
   348                 outputs = [{"output_name": output} for output in ["DONE", "BUSY", "ERROR"]] + \
       
   349                           [{"output_name": output["name"].upper()} for output in blocktype_infos["outputs"]]
       
   350                 output_texts = []
       
   351                 for output_infos in outputs:
       
   352                     output_infos.update(texts)
       
   353                     output_texts.append(BLOCK_OUTPUT_TEMPLATE % output_infos)
       
   354                 texts["return_outputs"] = "\n".join(output_texts)
       
   355                 
       
   356                 str_completion["fieldbus_interface_declaration"].append(
       
   357                         BLOCK_FUNCTION_TEMPLATE % texts)
       
   358                 
       
   359                 str_completion["fieldbus_interface_definition"].append(
       
   360                         BLOCK_FUNTION_DEFINITION_TEMPLATE % texts)
       
   361                 
       
   362             variables = NODE_VARIABLES[:]
       
   363             
       
   364             params = self.CTNParams[1].getElementInfos(self.CTNParams[0])
       
   365             for param in params["children"]:
       
   366                 if param["name"] in EXTRA_NODE_VARIABLES_DICT:
       
   367                     if param["value"]:
       
   368                         extra_variables = EXTRA_NODE_VARIABLES_DICT.get(param["name"])
       
   369                         for variable_infos in extra_variables:
       
   370                             var_infos = {
       
   371                                 "location": location_str,
       
   372                                 "name": variable_infos["description"][0]
       
   373                             }
       
   374                             variables.append(variable_infos["description"])
       
   375                             retrieve_template = variable_infos.get("retrieve", DEFAULT_RETRIEVE)
       
   376                             publish_template = variable_infos.get("publish", DEFAULT_PUBLISH)
       
   377                             
       
   378                             if retrieve_template is not None:
       
   379                                 str_completion["extra_variables_retrieve"].append(
       
   380                                     retrieve_template % var_infos)
       
   381                             if publish_template is not None:
       
   382                                 str_completion["extra_variables_publish"].append(
       
   383                                     publish_template % var_infos)
       
   384                 elif param["value"] is not None:
       
   385                     param_infos = {
       
   386                         "location": location_str,
       
   387                         "param_name": param["name"],
       
   388                     }
       
   389                     if param["type"] == "boolean":
       
   390                         param_infos["param_value"] = {True: "true", False: "false"}[param["value"]]
       
   391                     else:
       
   392                         param_infos["param_value"] = str(param["value"])
       
   393                     str_completion["init_axis_params"].append(
       
   394                         "        __CIA402Node_%(location)s.axis->%(param_name)s = %(param_value)s;" % param_infos)
       
   395             
       
   396             for variable in variables:
       
   397                 var_infos = dict(zip(["name", "index", "subindex", "var_type", "dir"], variable))
       
   398                 var_infos["location"] = location_str
       
   399                 var_infos["var_size"] = self.GetSizeOfType(var_infos["var_type"])
       
   400                 var_infos["var_name"] = "__%(dir)s%(var_size)s%(location)s_%(index)d_%(subindex)d" % var_infos
       
   401                 
       
   402                 str_completion["extern_located_variables_declaration"].append(
       
   403                         "IEC_%(var_type)s *%(var_name)s;" % var_infos)
       
   404                 str_completion["entry_variables"].append(
       
   405                         "    IEC_%(var_type)s *%(name)s;" % var_infos)
       
   406                 str_completion["init_entry_variables"].append(
       
   407                         "    __CIA402Node_%(location)s.%(name)s = %(var_name)s;" % var_infos)
       
   408                 
       
   409                 self.CTNParent.FileGenerator.DeclareVariable(
       
   410                         self.GetSlavePos(), var_infos["index"], var_infos["subindex"], 
       
   411                         var_infos["var_type"], var_infos["dir"], var_infos["var_name"])
       
   412             
       
   413             for element in ["extern_located_variables_declaration", 
       
   414                             "fieldbus_interface_declaration",
       
   415                             "fieldbus_interface_definition",
       
   416                             "entry_variables", 
       
   417                             "init_axis_params", 
       
   418                             "init_entry_variables",
       
   419                             "extra_variables_retrieve",
       
   420                             "extra_variables_publish"]:
       
   421                 str_completion[element] = "\n".join(str_completion[element])
       
   422             
       
   423             Gen_CIA402Nodefile_path = os.path.join(buildpath, "cia402node_%s.c"%location_str)
       
   424             cia402nodefile = open(Gen_CIA402Nodefile_path, 'w')
       
   425             cia402nodefile.write(plc_cia402node_code % str_completion)
       
   426             cia402nodefile.close()
       
   427             
       
   428             return [(Gen_CIA402Nodefile_path, '"-I%s"'%os.path.abspath(self.GetCTRoot().GetIECLibPath()))],"",True
       
   429 
       
   430 #--------------------------------------------------
       
   431 #                 Ethercat MASTER
       
   432 #--------------------------------------------------
       
   433 
       
   434 EtherCATConfigClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATConfig.xsd")) 
       
   435 
       
   436 def ExtractHexDecValue(value):
       
   437     try:
       
   438         return int(value)
       
   439     except:
       
   440         pass
       
   441     try:
       
   442         return int(value.replace("#", "0"), 16)
       
   443     except:
       
   444         raise ValueError, "Invalid value for HexDecValue \"%s\"" % value
       
   445 
       
   446 def GenerateHexDecValue(value, base=10):
       
   447     if base == 10:
       
   448         return str(value)
       
   449     elif base == 16:
       
   450         return "#x%.8x" % value
       
   451     else:
       
   452         raise ValueError, "Not supported base"
       
   453 
       
   454 def sort_commands(x, y):
       
   455     if x["Index"] == y["Index"]:
       
   456         return cmp(x["Subindex"], y["Subindex"])
       
   457     return cmp(x["Index"], y["Index"])
       
   458 
       
   459 cls = EtherCATConfigClasses.get("Config_Slave", None)
       
   460 if cls:
       
   461     
       
   462     def getType(self):
       
   463         slave_info = self.getInfo()
       
   464         return {"device_type": slave_info.getName(),
       
   465                 "vendor": GenerateHexDecValue(slave_info.getVendorId()),
       
   466                 "product_code": GenerateHexDecValue(slave_info.getProductCode(), 16),
       
   467                 "revision_number": GenerateHexDecValue(slave_info.getRevisionNo(), 16)}
       
   468     setattr(cls, "getType", getType)
       
   469 
       
   470     def setType(self, type_infos):
       
   471         slave_info = self.getInfo()
       
   472         slave_info.setName(type_infos["device_type"])
       
   473         slave_info.setVendorId(ExtractHexDecValue(type_infos["vendor"]))
       
   474         slave_info.setProductCode(ExtractHexDecValue(type_infos["product_code"]))
       
   475         slave_info.setRevisionNo(ExtractHexDecValue(type_infos["revision_number"]))
       
   476     setattr(cls, "setType", setType)
       
   477     
       
   478     def getInitCmds(self, create_default=False):
       
   479         Mailbox = self.getMailbox()
       
   480         if Mailbox is None:
       
   481             if create_default:
       
   482                 self.addMailbox()
       
   483                 Mailbox = self.getMailbox()
       
   484             else:
       
   485                 return None
       
   486         CoE = Mailbox.getCoE()
       
   487         if CoE is None:
       
   488             if create_default:
       
   489                 Mailbox.addCoE()
       
   490                 CoE = Mailbox.getCoE()
       
   491             else:
       
   492                 return None
       
   493         InitCmds = CoE.getInitCmds()
       
   494         if InitCmds is None and create_default:
       
   495             CoE.addInitCmds()
       
   496             InitCmds = CoE.getInitCmds()
       
   497         return InitCmds
       
   498     setattr(cls, "getInitCmds", getInitCmds)
       
   499     
       
   500     def getStartupCommands(self):
       
   501         pos = self.getInfo().getPhysAddr()
       
   502         InitCmds = self.getInitCmds()
       
   503         if InitCmds is None:
       
   504             return []
       
   505         commands = []
       
   506         for idx, InitCmd in enumerate(InitCmds.getInitCmd()):
       
   507             comment = InitCmd.getComment()
       
   508             if comment is None:
       
   509                 comment = ""
       
   510             commands.append({
       
   511                 "command_idx": idx,
       
   512                 "Position": pos,
       
   513                 "Index": InitCmd.getIndex(),
       
   514                 "Subindex": InitCmd.getSubIndex(),
       
   515                 "Value": InitCmd.getData(),
       
   516                 "Description": comment})
       
   517         commands.sort(sort_commands)
       
   518         return commands
       
   519     setattr(cls, "getStartupCommands", getStartupCommands)
       
   520     
       
   521     def appendStartupCommand(self, command_infos):
       
   522         InitCmds = self.getInitCmds(True)
       
   523         command = EtherCATConfigClasses["InitCmds_InitCmd"]()
       
   524         command.setIndex(command_infos["Index"])
       
   525         command.setSubIndex(command_infos["Subindex"])
       
   526         command.setData(command_infos["Value"])
       
   527         command.setComment(command_infos["Description"])
       
   528         InitCmds.appendInitCmd(command)
       
   529         return len(InitCmds.getInitCmd()) - 1
       
   530     setattr(cls, "appendStartupCommand", appendStartupCommand)
       
   531     
       
   532     def setStartupCommand(self, command_infos):
       
   533         InitCmds = self.getInitCmds()
       
   534         if InitCmds is not None:
       
   535             commands = InitCmds.getInitCmd()
       
   536             if command_infos["command_idx"] < len(commands):
       
   537                 command = commands[command_infos["command_idx"]]
       
   538                 command.setIndex(command_infos["Index"])
       
   539                 command.setSubIndex(command_infos["Subindex"])
       
   540                 command.setData(command_infos["Value"])
       
   541                 command.setComment(command_infos["Description"])
       
   542     setattr(cls, "setStartupCommand", setStartupCommand)
       
   543     
       
   544     def removeStartupCommand(self, command_idx):
       
   545         InitCmds = self.getInitCmds()
       
   546         if InitCmds is not None:
       
   547             if command_idx < len(InitCmds.getInitCmd()):
       
   548                 InitCmds.removeInitCmd(command_idx)
       
   549     setattr(cls, "removeStartupCommand", removeStartupCommand)
       
   550 
       
   551 ProcessVariablesXSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
       
   552     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       
   553       <xsd:element name="ProcessVariables">
       
   554         <xsd:complexType>
       
   555           <xsd:sequence>
       
   556             <xsd:element name="variable" minOccurs="0" maxOccurs="unbounded">
       
   557               <xsd:complexType>
       
   558                 <xsd:sequence>
       
   559                   <xsd:element name="ReadFrom" type="LocationDesc" minOccurs="0"/>
       
   560                   <xsd:element name="WriteTo" type="LocationDesc" minOccurs="0"/>
       
   561                 </xsd:sequence>
       
   562                 <xsd:attribute name="Name" type="xsd:string" use="required"/>
       
   563                 <xsd:attribute name="Comment" type="xsd:string" use="required"/>
       
   564               </xsd:complexType>
       
   565             </xsd:element>
       
   566           </xsd:sequence>
       
   567         </xsd:complexType>
       
   568       </xsd:element>
       
   569       <xsd:complexType name="LocationDesc">
       
   570         <xsd:attribute name="Position" type="xsd:integer" use="required"/>
       
   571         <xsd:attribute name="Index" type="xsd:integer" use="required"/>
       
   572         <xsd:attribute name="SubIndex" type="xsd:integer" use="required"/>
       
   573       </xsd:complexType>
       
   574     </xsd:schema>
       
   575 """
       
   576 
       
   577 ProcessVariablesClasses = GenerateClassesFromXSDstring(ProcessVariablesXSD) 
       
   578 
       
   579 class _EthercatCTN:
       
   580     
       
   581     CTNChildrenTypes = [("EthercatSlave", _EthercatSlaveCTN, "Ethercat Slave")]
       
   582     if HAS_MCL:
       
   583         CTNChildrenTypes.append(("EthercatCIA402Slave", _EthercatCIA402SlaveCTN, "Ethercat CIA402 Slave"))
       
   584     EditorType = MasterEditor
       
   585     
       
   586     def __init__(self):
       
   587         config_filepath = self.ConfigFileName()
       
   588         config_is_saved = False
       
   589         self.Config = EtherCATConfigClasses["EtherCATConfig"]()
       
   590         if os.path.isfile(config_filepath):
       
   591             config_xmlfile = open(config_filepath, 'r')
       
   592             config_tree = minidom.parse(config_xmlfile)
       
   593             config_xmlfile.close()
       
   594             
       
   595             for child in config_tree.childNodes:
       
   596                 if child.nodeType == config_tree.ELEMENT_NODE and child.nodeName == "EtherCATConfig":
       
   597                     self.Config.loadXMLTree(child)
       
   598                     config_is_saved = True
       
   599         
       
   600         process_filepath = self.ProcessVariablesFileName()
       
   601         process_is_saved = False
       
   602         self.ProcessVariables = ProcessVariablesClasses["ProcessVariables"]()
       
   603         if os.path.isfile(process_filepath):
       
   604             process_xmlfile = open(process_filepath, 'r')
       
   605             process_tree = minidom.parse(process_xmlfile)
       
   606             process_xmlfile.close()
       
   607             
       
   608             for child in process_tree.childNodes:
       
   609                 if child.nodeType == process_tree.ELEMENT_NODE and child.nodeName == "ProcessVariables":
       
   610                     self.ProcessVariables.loadXMLTree(child)
       
   611                     process_is_saved = True
       
   612         
       
   613         if config_is_saved and process_is_saved:
       
   614             self.CreateBuffer(True)
       
   615         else:
       
   616             self.CreateBuffer(False)
       
   617             self.OnCTNSave()
       
   618     
       
   619     def GetContextualMenuItems(self):
       
   620         return [("Add Ethercat Slave", "Add Ethercat Slave to Master", self.OnAddEthercatSlave)]
       
   621     
       
   622     def OnAddEthercatSlave(self, event):
       
   623         app_frame = self.GetCTRoot().AppFrame
       
   624         dialog = BrowseValuesLibraryDialog(app_frame, 
       
   625             "Ethercat Slave Type", self.GetSlaveTypesLibrary())
       
   626         if dialog.ShowModal() == wx.ID_OK:
       
   627             type_infos = dialog.GetValueInfos()
       
   628             device, alignment = self.GetModuleInfos(type_infos)
       
   629             if device is not None:
       
   630                 if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers():
       
   631                     ConfNodeType = "EthercatCIA402Slave"
       
   632                 else:
       
   633                     ConfNodeType = "EthercatSlave"
       
   634                 new_child = self.CTNAddChild("%s_0" % ConfNodeType, ConfNodeType)
       
   635                 new_child.SetParamsAttribute("SlaveParams.Type", type_infos)
       
   636                 self.CTNRequestSave()
       
   637                 new_child._OpenView()
       
   638                 app_frame._Refresh(TITLE, FILEMENU, PROJECTTREE)
       
   639         dialog.Destroy()
       
   640     
       
   641     def ExtractHexDecValue(self, value):
       
   642         return ExtractHexDecValue(value)
       
   643 
       
   644     def GetSizeOfType(self, type):
       
   645         return TYPECONVERSION.get(self.GetCTRoot().GetBaseType(type), None)
       
   646 
       
   647     def ConfigFileName(self):
       
   648         return os.path.join(self.CTNPath(), "config.xml")
       
   649     
       
   650     def ProcessVariablesFileName(self):
       
   651         return os.path.join(self.CTNPath(), "process_variables.xml")
       
   652     
       
   653     def FilterSlave(self, slave, vendor=None, slave_pos=None, slave_profile=None):
       
   654         if slave_pos is not None and slave.getInfo().getPhysAddr() != slave_pos:
       
   655             return False
       
   656         type_infos = slave.getType()
       
   657         if vendor is not None and ExtractHexDecValue(type_infos["vendor"]) != vendor:
       
   658             return False
       
   659         device, alignment = self.GetModuleInfos(type_infos)
       
   660         if slave_profile is not None and slave_profile not in device.GetProfileNumbers():
       
   661             return False
       
   662         return True
       
   663 
       
   664     def GetSlaves(self, vendor=None, slave_pos=None, slave_profile=None):
       
   665         slaves = []
       
   666         for slave in self.Config.getConfig().getSlave():
       
   667             if self.FilterSlave(slave, vendor, slave_pos, slave_profile):
       
   668                 slaves.append(slave.getInfo().getPhysAddr())
       
   669         slaves.sort()
       
   670         return slaves
       
   671 
       
   672     def GetSlave(self, slave_pos):
       
   673         for slave in self.Config.getConfig().getSlave():
       
   674             slave_info = slave.getInfo()
       
   675             if slave_info.getPhysAddr() == slave_pos:
       
   676                 return slave
       
   677         return None
       
   678 
       
   679     def GetStartupCommands(self, vendor=None, slave_pos=None, slave_profile=None):
       
   680         commands = []
       
   681         for slave in self.Config.getConfig().getSlave():
       
   682             if self.FilterSlave(slave, vendor, slave_pos, slave_profile):
       
   683                 commands.append((slave.getInfo().getPhysAddr(), slave.getStartupCommands()))
       
   684         commands.sort()
       
   685         return reduce(lambda x, y: x + y[1], commands, [])
       
   686     
       
   687     def AppendStartupCommand(self, command_infos):
       
   688         slave = self.GetSlave(command_infos["Position"])
       
   689         if slave is not None:
       
   690             command_idx = slave.appendStartupCommand(command_infos)
       
   691             self.BufferModel()
       
   692             return command_idx
       
   693         return None
       
   694     
       
   695     def SetStartupCommandInfos(self, command_infos):
       
   696         slave = self.GetSlave(command_infos["Position"])
       
   697         if slave is not None:
       
   698             slave.setStartupCommand(command_infos)
       
   699             self.BufferModel()
       
   700     
       
   701     def RemoveStartupCommand(self, slave_pos, command_idx, buffer=True):
       
   702         slave = self.GetSlave(slave_pos)
       
   703         if slave is not None:
       
   704             slave.removeStartupCommand(command_idx)
       
   705             if buffer:
       
   706                 self.BufferModel()
       
   707     
       
   708     def SetProcessVariables(self, variables):
       
   709         vars = []
       
   710         for var in variables:
       
   711             variable = ProcessVariablesClasses["ProcessVariables_variable"]()
       
   712             variable.setName(var["Name"])
       
   713             variable.setComment(var["Description"])
       
   714             if var["ReadFrom"] != "":
       
   715                 position, index, subindex = var["ReadFrom"]
       
   716                 if variable.getReadFrom() is None:
       
   717                     variable.addReadFrom()
       
   718                 read_from = variable.getReadFrom()
       
   719                 read_from.setPosition(position)
       
   720                 read_from.setIndex(index)
       
   721                 read_from.setSubIndex(subindex)
       
   722             elif variable.getReadFrom() is not None:
       
   723                 variable.deleteReadFrom()
       
   724             if var["WriteTo"] != "":
       
   725                 position, index, subindex = var["WriteTo"]
       
   726                 if variable.getWriteTo() is None:
       
   727                     variable.addWriteTo()
       
   728                 write_to = variable.getWriteTo()
       
   729                 write_to.setPosition(position)
       
   730                 write_to.setIndex(index)
       
   731                 write_to.setSubIndex(subindex)
       
   732             elif variable.getWriteTo() is not None:
       
   733                 variable.deleteWriteTo()
       
   734             vars.append(variable)
       
   735         self.ProcessVariables.setvariable(vars)
       
   736         self.BufferModel()
       
   737         
       
   738     def GetProcessVariables(self):
       
   739         variables = []
       
   740         idx = 0
       
   741         for variable in self.ProcessVariables.getvariable():
       
   742             var = {"Name": variable.getName(),
       
   743                    "Number": idx,
       
   744                    "Description": variable.getComment()}
       
   745             read_from = variable.getReadFrom()
       
   746             if read_from is not None:
       
   747                 var["ReadFrom"] = (read_from.getPosition(),
       
   748                                    read_from.getIndex(),
       
   749                                    read_from.getSubIndex())
       
   750             else:
       
   751                 var["ReadFrom"] = ""
       
   752             write_to = variable.getWriteTo()
       
   753             if write_to is not None:
       
   754                 var["WriteTo"] = (write_to.getPosition(),
       
   755                                    write_to.getIndex(),
       
   756                                    write_to.getSubIndex())
       
   757             else:
       
   758                 var["WriteTo"] = ""
       
   759             variables.append(var)
       
   760             idx += 1
       
   761         return variables
       
   762     
       
   763     def _ScanNetwork(self):
       
   764         app_frame = self.GetCTRoot().AppFrame
       
   765         
       
   766         execute = True
       
   767         if len(self.Children) > 0:
       
   768             dialog = wx.MessageDialog(app_frame, 
       
   769                 _("The current network configuration will be deleted.\nDo you want to continue?"), 
       
   770                 _("Scan Network"), 
       
   771                 wx.YES_NO|wx.ICON_QUESTION)
       
   772             execute = dialog.ShowModal() == wx.ID_YES
       
   773             dialog.Destroy()
       
   774         
       
   775         if execute:
       
   776             error, returnVal = self.RemoteExec(SCAN_COMMAND, returnVal = None)
       
   777             if error != 0:
       
   778                 dialog = wx.MessageDialog(app_frame, returnVal, "Error", wx.OK|wx.ICON_ERROR)
       
   779                 dialog.ShowModal()
       
   780                 dialog.Destroy()
       
   781             elif returnVal is not None:
       
   782                 for child in self.IECSortedChildren():
       
   783                     self._doRemoveChild(child)
       
   784                 
       
   785                 for slave in returnVal:
       
   786                     type_infos = {
       
   787                         "vendor": slave["vendor_id"],
       
   788                         "product_code": slave["product_code"],
       
   789                         "revision_number":slave["revision_number"],
       
   790                     }
       
   791                     device, alignment = self.GetModuleInfos(type_infos)
       
   792                     if device is not None:
       
   793                         if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers():
       
   794                             CTNType = "EthercatCIA402Slave"
       
   795                         else:
       
   796                             CTNType = "EthercatSlave"
       
   797                         self.CTNAddChild("slave%s" % slave["idx"], CTNType, slave["idx"])
       
   798                         self.SetSlaveAlias(slave["idx"], slave["alias"])
       
   799                         type_infos["device_type"] = device.getType().getcontent()
       
   800                         self.SetSlaveType(slave["idx"], type_infos)
       
   801 
       
   802     def CTNAddChild(self, CTNName, CTNType, IEC_Channel=0):
       
   803         """
       
   804         Create the confnodes that may be added as child to this node self
       
   805         @param CTNType: string desining the confnode class name (get name from CTNChildrenTypes)
       
   806         @param CTNName: string for the name of the confnode instance
       
   807         """
       
   808         newConfNodeOpj = ConfigTreeNode.CTNAddChild(self, CTNName, CTNType, IEC_Channel)
       
   809         
       
   810         slave = self.GetSlave(newConfNodeOpj.BaseParams.getIEC_Channel())
       
   811         if slave is None:
       
   812             slave = EtherCATConfigClasses["Config_Slave"]()
       
   813             slave_infos = slave.getInfo()
       
   814             slave_infos.setName("undefined")
       
   815             slave_infos.setPhysAddr(newConfNodeOpj.BaseParams.getIEC_Channel())
       
   816             slave_infos.setAutoIncAddr(0)
       
   817             self.Config.getConfig().appendSlave(slave)
       
   818             self.BufferModel()
       
   819             self.OnCTNSave()
       
   820         
       
   821         return newConfNodeOpj
       
   822 
       
   823     def _doRemoveChild(self, CTNInstance):
       
   824         slave_pos = CTNInstance.GetSlavePos()
       
   825         config = self.Config.getConfig()
       
   826         for idx, slave in enumerate(config.getSlave()):
       
   827             slave_infos = slave.getInfo()
       
   828             if slave_infos.getPhysAddr() == slave_pos:
       
   829                 config.removeSlave(idx)
       
   830                 self.BufferModel()
       
   831                 self.OnCTNSave()
       
   832         ConfigTreeNode._doRemoveChild(self, CTNInstance)
       
   833 
       
   834     def SetSlavePosition(self, slave_pos, new_pos):
       
   835         slave = self.GetSlave(slave_pos)
       
   836         if slave is not None:
       
   837             slave_info = slave.getInfo()
       
   838             slave_info.setPhysAddr(new_pos)
       
   839             for variable in self.ProcessVariables.getvariable():
       
   840                 read_from = variable.getReadFrom()
       
   841                 if read_from is not None and read_from.getPosition() == slave_pos:
       
   842                     read_from.setPosition(new_pos)
       
   843                 write_to = variable.getWriteTo()
       
   844                 if write_to is not None and write_to.getPosition() == slave_pos:
       
   845                     write_to.setPosition(new_pos)
       
   846             self.CreateBuffer(True)
       
   847             self.OnCTNSave()
       
   848             if self._View is not None:
       
   849                 self._View.RefreshView()
       
   850                 self._View.RefreshBuffer()
       
   851     
       
   852     def GetSlaveAlias(self, slave_pos):
       
   853         slave = self.GetSlave(slave_pos)
       
   854         if slave is not None:
       
   855             slave_info = slave.getInfo()
       
   856             return slave_info.getAutoIncAddr()
       
   857         return None
       
   858     
       
   859     def SetSlaveAlias(self, slave_pos, alias):
       
   860         slave = self.GetSlave(slave_pos)
       
   861         if slave is not None:
       
   862             slave_info = slave.getInfo()
       
   863             slave_info.setAutoIncAddr(alias)
       
   864             self.BufferModel()
       
   865     
       
   866     def GetSlaveType(self, slave_pos):
       
   867         slave = self.GetSlave(slave_pos)
       
   868         if slave is not None:
       
   869             return slave.getType()
       
   870         return None
       
   871     
       
   872     def SetSlaveType(self, slave_pos, type_infos):
       
   873         slave = self.GetSlave(slave_pos)
       
   874         if slave is not None:
       
   875             slave.setType(type_infos)
       
   876             self.BufferModel()
       
   877     
       
   878     def GetSlaveInfos(self, slave_pos):
       
   879         slave = self.GetSlave(slave_pos)
       
   880         if slave is not None:
       
   881             type_infos = slave.getType()
       
   882             device, alignment = self.GetModuleInfos(type_infos)
       
   883             if device is not None:
       
   884                 infos = type_infos.copy()
       
   885                 infos.update({"physics": device.getPhysics(),
       
   886                               "sync_managers": device.GetSyncManagers(),
       
   887                               "entries": self.GetSlaveVariables(device)})
       
   888                 return infos
       
   889         return None
       
   890     
       
   891     def GetSlaveVariables(self, slave_pos=None, limits=None, device=None):
       
   892         if device is None and slave_pos is not None:
       
   893             slave = self.GetSlave(slave_pos)
       
   894             if slave is not None:
       
   895                 type_infos = slave.getType()
       
   896                 device, alignment = self.GetModuleInfos(type_infos)
       
   897         if device is not None:
       
   898             entries = device.GetEntriesList(limits)
       
   899             entries_list = entries.items()
       
   900             entries_list.sort()
       
   901             entries = []
       
   902             current_index = None
       
   903             current_entry = None
       
   904             for (index, subindex), entry in entries_list:
       
   905                 entry["children"] = []
       
   906                 if slave_pos is not None:
       
   907                     entry["Position"] = str(slave_pos)
       
   908                 entry
       
   909                 if index != current_index:
       
   910                     current_index = index
       
   911                     current_entry = entry
       
   912                     entries.append(entry)
       
   913                 elif current_entry is not None:
       
   914                     current_entry["children"].append(entry)
       
   915                 else:
       
   916                     entries.append(entry)
       
   917             return entries
       
   918         return []
       
   919     
       
   920     def GetSlaveVariableDataType(self, slave_pos, index, subindex):
       
   921         slave = self.GetSlave(slave_pos)
       
   922         if slave is not None:
       
   923             device, alignment = self.GetModuleInfos(slave.getType())
       
   924             if device is not None:
       
   925                 entries = device.GetEntriesList()
       
   926                 entry_infos = entries.get((index, subindex))
       
   927                 if entry_infos is not None:
       
   928                     return entry_infos["Type"]
       
   929         return None
       
   930     
       
   931     def GetNodesVariables(self, vendor=None, slave_pos=None, slave_profile=None, limits=None):
       
   932         entries = []
       
   933         for slave_position in self.GetSlaves():
       
   934             if slave_pos is not None and slave_position != slave_pos:
       
   935                 continue
       
   936             slave = self.GetSlave(slave_position)
       
   937             type_infos = slave.getType()
       
   938             if vendor is not None and ExtractHexDecValue(type_infos["vendor"]) != vendor:
       
   939                 continue
       
   940             device, alignment = self.GetModuleInfos(type_infos)
       
   941             if slave_profile is not None and slave_profile not in device.GetProfileNumbers():
       
   942                 continue
       
   943             entries.extend(self.GetSlaveVariables(slave_position, limits, device))
       
   944         return entries
       
   945      
       
   946     def GetModuleInfos(self, type_infos):
       
   947         return self.CTNParent.GetModuleInfos(type_infos)
       
   948     
       
   949     def GetSlaveTypesLibrary(self, profile_filter=None):
       
   950         return self.CTNParent.GetModulesLibrary(profile_filter)
       
   951     
       
   952     def GetLibraryVendors(self):
       
   953         return self.CTNParent.GetVendors()
       
   954     
       
   955     def GetDeviceLocationTree(self, slave_pos, current_location, device_name):
       
   956         slave = self.GetSlave(slave_pos)
       
   957         vars = []    
       
   958         if slave is not None:
       
   959             type_infos = slave.getType()
       
   960         
       
   961             device, alignment = self.GetModuleInfos(type_infos)
       
   962             if device is not None:
       
   963                 sync_managers = []
       
   964                 for sync_manager in device.getSm():
       
   965                     sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte())
       
   966                     sync_manager_direction = sync_manager_control_byte & 0x0c
       
   967                     if sync_manager_direction:
       
   968                         sync_managers.append(LOCATION_VAR_OUTPUT)
       
   969                     else:
       
   970                         sync_managers.append(LOCATION_VAR_INPUT)
       
   971                 
       
   972                 entries = device.GetEntriesList().items()
       
   973                 entries.sort()
       
   974                 for (index, subindex), entry in entries:
       
   975                     var_size = self.GetSizeOfType(entry["Type"])
       
   976                     if var_size is not None:
       
   977                         var_class = VARCLASSCONVERSION.get(entry["PDOMapping"], None)
       
   978                         if var_class is not None:
       
   979                             if var_class == LOCATION_VAR_INPUT:
       
   980                                 var_dir = "%I"
       
   981                             else:
       
   982                                 var_dir = "%Q"    
       
   983                         
       
   984                             vars.append({"name": "0x%4.4x-0x%2.2x: %s" % (index, subindex, entry["Name"]),
       
   985                                          "type": var_class,
       
   986                                          "size": var_size,
       
   987                                          "IEC_type": entry["Type"],
       
   988                                          "var_name": "%s_%4.4x_%2.2x" % ("_".join(device_name.split()), index, subindex),
       
   989                                          "location": "%s%s%s"%(var_dir, var_size, ".".join(map(str, current_location + 
       
   990                                                                                                     (index, subindex)))),
       
   991                                          "description": "",
       
   992                                          "children": []})
       
   993         
       
   994         return vars
       
   995     
       
   996     def CTNTestModified(self):
       
   997         return self.ChangesToSave or not self.ModelIsSaved()    
       
   998 
       
   999     def OnCTNSave(self):
       
  1000         config_filepath = self.ConfigFileName()
       
  1001         
       
  1002         config_text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
       
  1003         config_extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",
       
  1004                   "xsi:noNamespaceSchemaLocation" : "EtherCATInfo.xsd"}
       
  1005         config_text += self.Config.generateXMLText("EtherCATConfig", 0, config_extras)
       
  1006 
       
  1007         config_xmlfile = open(config_filepath,"w")
       
  1008         config_xmlfile.write(config_text.encode("utf-8"))
       
  1009         config_xmlfile.close()
       
  1010         
       
  1011         process_filepath = self.ProcessVariablesFileName()
       
  1012         
       
  1013         process_text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
       
  1014         process_extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance"}
       
  1015         process_text += self.ProcessVariables.generateXMLText("ProcessVariables", 0, process_extras)
       
  1016 
       
  1017         process_xmlfile = open(process_filepath,"w")
       
  1018         process_xmlfile.write(process_text.encode("utf-8"))
       
  1019         process_xmlfile.close()
       
  1020         
       
  1021         self.Buffer.CurrentSaved()
       
  1022         return True
       
  1023 
       
  1024     def GetProcessVariableName(self, location, var_type):
       
  1025         return "__M%s_%s" % (self.GetSizeOfType(var_type), "_".join(map(str, location)))
       
  1026 
       
  1027     def _Generate_C(self, buildpath, locations):
       
  1028         current_location = self.GetCurrentLocation()
       
  1029         # define a unique name for the generated C file
       
  1030         location_str = "_".join(map(lambda x:str(x), current_location))
       
  1031         
       
  1032         Gen_Ethercatfile_path = os.path.join(buildpath, "ethercat_%s.c"%location_str)
       
  1033         
       
  1034         self.FileGenerator = _EthercatCFileGenerator(self)
       
  1035         
       
  1036         LocationCFilesAndCFLAGS, LDFLAGS, extra_files = ConfigTreeNode._Generate_C(self, buildpath, locations)
       
  1037         
       
  1038         for idx, variable in enumerate(self.ProcessVariables.getvariable()):
       
  1039             name = None
       
  1040             var_type = None
       
  1041             read_from = variable.getReadFrom()
       
  1042             write_to = variable.getWriteTo()
       
  1043             if read_from is not None:
       
  1044                 pos = read_from.getPosition()
       
  1045                 index = read_from.getIndex()
       
  1046                 subindex = read_from.getSubIndex()
       
  1047                 location = current_location + (idx, )
       
  1048                 var_type = self.GetSlaveVariableDataType(pos, index, subindex)
       
  1049                 name = self.FileGenerator.DeclareVariable(
       
  1050                             pos, index, subindex, var_type, "I",
       
  1051                             self.GetProcessVariableName(location, var_type))
       
  1052             if write_to is not None:
       
  1053                 pos = write_to.getPosition()
       
  1054                 index = write_to.getIndex()
       
  1055                 subindex = write_to.getSubIndex()
       
  1056                 if name is None:
       
  1057                     location = current_location + (idx, )
       
  1058                     var_type = self.GetSlaveVariableDataType(pos, index, subindex)
       
  1059                     name = self.GetProcessVariableName(location, var_type)
       
  1060                 self.FileGenerator.DeclareVariable(
       
  1061                             pos, index, subindex, var_type, "Q", name, True)
       
  1062         
       
  1063         self.FileGenerator.GenerateCFile(Gen_Ethercatfile_path, location_str, self.BaseParams.getIEC_Channel())
       
  1064         
       
  1065         LocationCFilesAndCFLAGS.append(
       
  1066             (current_location, 
       
  1067              [(Gen_Ethercatfile_path, '"-I%s"'%os.path.abspath(self.GetCTRoot().GetIECLibPath()))], 
       
  1068              True))
       
  1069         LDFLAGS.append("-lethercat -lrtdm")
       
  1070         
       
  1071         return LocationCFilesAndCFLAGS, LDFLAGS, extra_files
       
  1072 
       
  1073     ConfNodeMethods = [
       
  1074         {"bitmap" : "ScanNetwork",
       
  1075          "name" : _("Scan Network"), 
       
  1076          "tooltip" : _("Scan Network"),
       
  1077          "method" : "_ScanNetwork"},
       
  1078     ]
       
  1079 
       
  1080     def CTNGenerate_C(self, buildpath, locations):
       
  1081         current_location = self.GetCurrentLocation()
       
  1082         
       
  1083         slaves = self.GetSlaves()
       
  1084         for slave_pos in slaves:
       
  1085             slave = self.GetSlave(slave_pos)
       
  1086             if slave is not None:
       
  1087                 self.FileGenerator.DeclareSlave(slave_pos, slave)
       
  1088         
       
  1089         for location in locations:
       
  1090             loc = location["LOC"][len(current_location):]
       
  1091             slave_pos = loc[0]
       
  1092             if slave_pos in slaves and len(loc) == 3 and location["DIR"] != "M":
       
  1093                 self.FileGenerator.DeclareVariable(
       
  1094                     slave_pos, loc[1], loc[2], location["IEC_TYPE"], location["DIR"], location["NAME"])
       
  1095         
       
  1096         return [],"",False
       
  1097         
       
  1098 #-------------------------------------------------------------------------------
       
  1099 #                      Current Buffering Management Functions
       
  1100 #-------------------------------------------------------------------------------
       
  1101 
       
  1102     """
       
  1103     Return a copy of the config
       
  1104     """
       
  1105     def Copy(self, model):
       
  1106         return cPickle.loads(cPickle.dumps(model))
       
  1107     
       
  1108     def CreateBuffer(self, saved):
       
  1109         self.Buffer = UndoBuffer(cPickle.dumps((self.Config, self.ProcessVariables)), saved)
       
  1110         
       
  1111     def BufferModel(self):
       
  1112         self.Buffer.Buffering(cPickle.dumps((self.Config, self.ProcessVariables)))
       
  1113     
       
  1114     def ModelIsSaved(self):
       
  1115         if self.Buffer is not None:
       
  1116             return self.Buffer.IsCurrentSaved()
       
  1117         else:
       
  1118             return True
       
  1119 
       
  1120     def LoadPrevious(self):
       
  1121         self.Config, self.ProcessVariables = cPickle.loads(self.Buffer.Previous())
       
  1122     
       
  1123     def LoadNext(self):
       
  1124         self.Config, self.ProcessVariables = cPickle.loads(self.Buffer.Next())
       
  1125     
       
  1126     def GetBufferState(self):
       
  1127         first = self.Buffer.IsFirst()
       
  1128         last = self.Buffer.IsLast()
       
  1129         return not first, not last
       
  1130 
       
  1131 
       
  1132 SLAVE_PDOS_CONFIGURATION_DECLARATION = """
       
  1133 /* Slave %(slave)d, "%(device_type)s"
       
  1134  * Vendor ID:       0x%(vendor).8x
       
  1135  * Product code:    0x%(product_code).8x
       
  1136  * Revision number: 0x%(revision_number).8x
       
  1137  */
       
  1138 
       
  1139 ec_pdo_entry_info_t slave_%(slave)d_pdo_entries[] = {
       
  1140 %(pdos_entries_infos)s
       
  1141 };
       
  1142 
       
  1143 ec_pdo_info_t slave_%(slave)d_pdos[] = {
       
  1144 %(pdos_infos)s
       
  1145 };
       
  1146 
       
  1147 ec_sync_info_t slave_%(slave)d_syncs[] = {
       
  1148 %(pdos_sync_infos)s
       
  1149     {0xff}
       
  1150 };
       
  1151 """
       
  1152 
       
  1153 SLAVE_CONFIGURATION_TEMPLATE = """
       
  1154     if (!(slave%(slave)d = ecrt_master_slave_config(master, %(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x))) {
       
  1155         SLOGF(LOG_CRITICAL, "Failed to get slave %(device_type)s configuration at alias %(alias)d and position %(position)d.\\n");
       
  1156         return -1;
       
  1157     }
       
  1158 
       
  1159     if (ecrt_slave_config_pdos(slave%(slave)d, EC_END, slave_%(slave)d_syncs)) {
       
  1160         SLOGF(LOG_CRITICAL, "Failed to configure PDOs for slave %(device_type)s at alias %(alias)d and position %(position)d.\\n");
       
  1161         return -1;
       
  1162     }
       
  1163 """
       
  1164 
       
  1165 SLAVE_INITIALIZATION_TEMPLATE = """
       
  1166     {
       
  1167         uint8_t value[%(data_size)d];
       
  1168         EC_WRITE_%(data_type)s((uint8_t *)value, %(data)s);
       
  1169         if (ecrt_master_sdo_download(master, %(slave)d, 0x%(index).4x, 0x%(subindex).2x, (uint8_t *)value, %(data_size)d, &abort_code)) {
       
  1170             SLOGF(LOG_CRITICAL, "Failed to initialize slave %(device_type)s at alias %(alias)d and position %(position)d.\\nError: %%d\\n", abort_code);
       
  1171             return -1;
       
  1172         }
       
  1173     }
       
  1174 """
       
  1175 
       
  1176 SLAVE_OUTPUT_PDO_DEFAULT_VALUE = """
       
  1177     {
       
  1178         uint8_t value[%(data_size)d];
       
  1179         if (ecrt_master_sdo_upload(master, %(slave)d, 0x%(index).4x, 0x%(subindex).2x, (uint8_t *)value, %(data_size)d, &result_size, &abort_code)) {
       
  1180             SLOGF(LOG_CRITICAL, "Failed to get default value for output PDO in slave %(device_type)s at alias %(alias)d and position %(position)d.\\nError: %%ud\\n", abort_code);
       
  1181             return -1;
       
  1182         }
       
  1183         %(real_var)s = EC_READ_%(data_type)s((uint8_t *)value);
       
  1184     }
       
  1185 """
       
  1186 
       
  1187 def ConfigureVariable(entry_infos, str_completion):
       
  1188     entry_infos["data_type"] = DATATYPECONVERSION.get(entry_infos["var_type"], None)
       
  1189     if entry_infos["data_type"] is None:
       
  1190         raise ValueError, _("Type of location \"%s\" not yet supported!") % entry_infos["var_name"]
       
  1191     
       
  1192     if not entry_infos.get("no_decl", False):
       
  1193         if entry_infos.has_key("real_var"):
       
  1194             str_completion["located_variables_declaration"].append(
       
  1195                 "IEC_%(var_type)s %(real_var)s;" % entry_infos)
       
  1196         else:
       
  1197             entry_infos["real_var"] = "beremiz" + entry_infos["var_name"]
       
  1198             str_completion["located_variables_declaration"].extend(
       
  1199                 ["IEC_%(var_type)s %(real_var)s;" % entry_infos,
       
  1200                  "IEC_%(var_type)s *%(var_name)s = &%(real_var)s;" % entry_infos])
       
  1201         for declaration in entry_infos.get("extra_declarations", []):
       
  1202             entry_infos["extra_decl"] = declaration
       
  1203             str_completion["located_variables_declaration"].append(
       
  1204                  "IEC_%(var_type)s *%(extra_decl)s = &%(real_var)s;" % entry_infos)
       
  1205     elif not entry_infos.has_key("real_var"):
       
  1206         entry_infos["real_var"] = "beremiz" + entry_infos["var_name"]
       
  1207     
       
  1208     str_completion["used_pdo_entry_offset_variables_declaration"].append(
       
  1209         "unsigned int slave%(slave)d_%(index).4x_%(subindex).2x;" % entry_infos)
       
  1210     
       
  1211     if entry_infos["data_type"] == "BIT":
       
  1212         str_completion["used_pdo_entry_offset_variables_declaration"].append(
       
  1213             "unsigned int slave%(slave)d_%(index).4x_%(subindex).2x_bit;" % entry_infos)
       
  1214         
       
  1215         str_completion["used_pdo_entry_configuration"].append(
       
  1216              ("    {%(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x, " + 
       
  1217               "0x%(index).4x, %(subindex)d, &slave%(slave)d_%(index).4x_%(subindex).2x, " + 
       
  1218               "&slave%(slave)d_%(index).4x_%(subindex).2x_bit},") % entry_infos)
       
  1219         
       
  1220         if entry_infos["dir"] == "I":
       
  1221             str_completion["retrieve_variables"].append(
       
  1222               ("    %(real_var)s = EC_READ_BIT(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, " + 
       
  1223                "slave%(slave)d_%(index).4x_%(subindex).2x_bit);") % entry_infos)
       
  1224         elif entry_infos["dir"] == "Q":
       
  1225             str_completion["publish_variables"].append(
       
  1226               ("    EC_WRITE_BIT(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, " + 
       
  1227                "slave%(slave)d_%(index).4x_%(subindex).2x_bit, %(real_var)s);") % entry_infos)
       
  1228     
       
  1229     else:
       
  1230         str_completion["used_pdo_entry_configuration"].append(
       
  1231             ("    {%(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x, 0x%(index).4x, " + 
       
  1232              "%(subindex)d, &slave%(slave)d_%(index).4x_%(subindex).2x},") % entry_infos)
       
  1233         
       
  1234         if entry_infos["dir"] == "I":
       
  1235             str_completion["retrieve_variables"].append(
       
  1236                 ("    %(real_var)s = EC_READ_%(data_type)s(domain1_pd + " + 
       
  1237                  "slave%(slave)d_%(index).4x_%(subindex).2x);") % entry_infos)
       
  1238         elif entry_infos["dir"] == "Q":
       
  1239             str_completion["publish_variables"].append(
       
  1240                 ("    EC_WRITE_%(data_type)s(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, " + 
       
  1241                  "%(real_var)s);") % entry_infos)
       
  1242 
       
  1243 def ExclusionSortFunction(x, y):
       
  1244     if x["matching"] == y["matching"]:
       
  1245         if x["assigned"] and not y["assigned"]:
       
  1246             return -1
       
  1247         elif not x["assigned"] and y["assigned"]:
       
  1248             return 1
       
  1249         return cmp(x["count"], y["count"])
       
  1250     return -cmp(x["matching"], y["matching"])
       
  1251 
       
  1252 class _EthercatCFileGenerator:
       
  1253     
       
  1254     def __init__(self, controler):
       
  1255         self.Controler = controler
       
  1256         
       
  1257         self.Slaves = []
       
  1258         self.UsedVariables = {}
       
  1259 
       
  1260     def __del__(self):
       
  1261         self.Controler = None            
       
  1262     
       
  1263     def DeclareSlave(self, slave_index, slave):
       
  1264         self.Slaves.append((slave_index, slave.getInfo().getAutoIncAddr(), slave))
       
  1265 
       
  1266     def DeclareVariable(self, slave_index, index, subindex, iec_type, dir, name, no_decl=False):
       
  1267         slave_variables = self.UsedVariables.setdefault(slave_index, {})
       
  1268         
       
  1269         entry_infos = slave_variables.get((index, subindex), None)
       
  1270         if entry_infos is None:
       
  1271             slave_variables[(index, subindex)] = {
       
  1272                 "infos": (iec_type, dir, name, no_decl, []),
       
  1273                 "mapped": False}
       
  1274             return name
       
  1275         elif entry_infos["infos"][:2] == (iec_type, dir):
       
  1276             if name != entry_infos["infos"][2]:
       
  1277                 if dir == "I":
       
  1278                     entry_infos["infos"][3].append(name)
       
  1279                     return entry_infos["infos"][2]
       
  1280                 else:
       
  1281                     raise ValueError, _("Output variables can't be defined with different locations (%s and %s)") % (entry_infos["infos"][2], name)
       
  1282         else:
       
  1283             raise ValueError, _("Definition conflict for location \"%s\"") % name 
       
  1284         
       
  1285     def GenerateCFile(self, filepath, location_str, master_number):
       
  1286         
       
  1287         # Extract etherlab master code template
       
  1288         plc_etherlab_filepath = os.path.join(os.path.split(__file__)[0], "plc_etherlab.c")
       
  1289         plc_etherlab_file = open(plc_etherlab_filepath, 'r')
       
  1290         plc_etherlab_code = plc_etherlab_file.read()
       
  1291         plc_etherlab_file.close()
       
  1292         
       
  1293         # Initialize strings for formatting master code template
       
  1294         str_completion = {
       
  1295             "location": location_str,
       
  1296             "master_number": master_number,
       
  1297             "located_variables_declaration": [],
       
  1298             "used_pdo_entry_offset_variables_declaration": [],
       
  1299             "used_pdo_entry_configuration": [],
       
  1300             "pdos_configuration_declaration": "",
       
  1301             "slaves_declaration": "",
       
  1302             "slaves_configuration": "",
       
  1303             "slaves_output_pdos_default_values_extraction": "",
       
  1304             "slaves_initialization": "",
       
  1305             "retrieve_variables": [],
       
  1306             "publish_variables": [],
       
  1307         }
       
  1308         
       
  1309         # Initialize variable storing variable mapping state
       
  1310         for slave_entries in self.UsedVariables.itervalues():
       
  1311             for entry_infos in slave_entries.itervalues():
       
  1312                 entry_infos["mapped"] = False
       
  1313         
       
  1314         # Sort slaves by position (IEC_Channel)
       
  1315         self.Slaves.sort()
       
  1316         # Initialize dictionary storing alias auto-increment position values
       
  1317         alias = {}
       
  1318         
       
  1319         # Generating code for each slave
       
  1320         for (slave_idx, slave_alias, slave) in self.Slaves:
       
  1321             type_infos = slave.getType()
       
  1322             
       
  1323             # Defining slave alias and auto-increment position
       
  1324             if alias.get(slave_alias) is not None:
       
  1325                 alias[slave_alias] += 1
       
  1326             else:
       
  1327                 alias[slave_alias] = 0
       
  1328             slave_pos = (slave_alias, alias[slave_alias])
       
  1329             
       
  1330             # Extract slave device informations
       
  1331             device, alignment = self.Controler.GetModuleInfos(type_infos)
       
  1332             if device is not None:
       
  1333                 
       
  1334                 # Extract slaves variables to be mapped
       
  1335                 slave_variables = self.UsedVariables.get(slave_idx, {})
       
  1336                 
       
  1337                 # Extract slave device object dictionary entries
       
  1338                 device_entries = device.GetEntriesList()
       
  1339                 
       
  1340                 # Adding code for declaring slave in master code template strings
       
  1341                 for element in ["vendor", "product_code", "revision_number"]:
       
  1342                     type_infos[element] = ExtractHexDecValue(type_infos[element])
       
  1343                 type_infos.update(dict(zip(["slave", "alias", "position"], (slave_idx,) + slave_pos)))
       
  1344                 
       
  1345                 # Extract slave device CoE informations
       
  1346                 device_coe = device.getCoE()
       
  1347                 if device_coe is not None:
       
  1348                     
       
  1349                     # If device support CanOpen over Ethernet, adding code for calling 
       
  1350                     # init commands when initializing slave in master code template strings
       
  1351                     initCmds = []
       
  1352                     for initCmd in device_coe.getInitCmd():
       
  1353                         initCmds.append({
       
  1354                             "Index": ExtractHexDecValue(initCmd.getIndex()),
       
  1355                             "Subindex": ExtractHexDecValue(initCmd.getSubIndex()),
       
  1356                             "Value": initCmd.getData().getcontent()})
       
  1357                     initCmds.extend(slave.getStartupCommands())
       
  1358                     for initCmd in initCmds:
       
  1359                         index = initCmd["Index"]
       
  1360                         subindex = initCmd["Subindex"]
       
  1361                         entry = device_entries.get((index, subindex), None)
       
  1362                         if entry is not None:
       
  1363                             data_size = entry["BitSize"] / 8
       
  1364                             data_str = ("0x%%.%dx" % (data_size * 2)) % initCmd["Value"]
       
  1365                             init_cmd_infos = {
       
  1366                                 "index": index,
       
  1367                                 "subindex": subindex,
       
  1368                                 "data": data_str,
       
  1369                                 "data_type": DATATYPECONVERSION.get(entry["Type"]),
       
  1370                                 "data_size": data_size
       
  1371                             }
       
  1372                             init_cmd_infos.update(type_infos)
       
  1373                             str_completion["slaves_initialization"] += SLAVE_INITIALIZATION_TEMPLATE % init_cmd_infos
       
  1374                 
       
  1375                     # Extract slave device PDO configuration capabilities
       
  1376                     PdoAssign = device_coe.getPdoAssign()
       
  1377                     PdoConfig = device_coe.getPdoConfig()
       
  1378                 else:
       
  1379                     PdoAssign = PdoConfig = False
       
  1380                 
       
  1381                 # Test if slave has a configuration or need one
       
  1382                 if len(device.getTxPdo() + device.getRxPdo()) > 0 or len(slave_variables) > 0 and PdoConfig and PdoAssign:
       
  1383                     
       
  1384                     str_completion["slaves_declaration"] += "static ec_slave_config_t *slave%(slave)d = NULL;\n" % type_infos
       
  1385                     str_completion["slaves_configuration"] += SLAVE_CONFIGURATION_TEMPLATE % type_infos
       
  1386                     
       
  1387                     # Initializing 
       
  1388                     pdos_infos = {
       
  1389                         "pdos_entries_infos": [],
       
  1390                         "pdos_infos": [],
       
  1391                         "pdos_sync_infos": [], 
       
  1392                     }
       
  1393                     pdos_infos.update(type_infos)
       
  1394                     
       
  1395                     sync_managers = []
       
  1396                     for sync_manager_idx, sync_manager in enumerate(device.getSm()):
       
  1397                         sync_manager_infos = {
       
  1398                             "index": sync_manager_idx, 
       
  1399                             "name": sync_manager.getcontent(),
       
  1400                             "slave": slave_idx,
       
  1401                             "pdos": [], 
       
  1402                             "pdos_number": 0,
       
  1403                         }
       
  1404                         
       
  1405                         sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte())
       
  1406                         sync_manager_direction = sync_manager_control_byte & 0x0c
       
  1407                         sync_manager_watchdog = sync_manager_control_byte & 0x40
       
  1408                         if sync_manager_direction:
       
  1409                             sync_manager_infos["sync_manager_type"] = "EC_DIR_OUTPUT"
       
  1410                         else:
       
  1411                             sync_manager_infos["sync_manager_type"] = "EC_DIR_INPUT"
       
  1412                         if sync_manager_watchdog:
       
  1413                             sync_manager_infos["watchdog"] = "EC_WD_ENABLE"
       
  1414                         else:
       
  1415                             sync_manager_infos["watchdog"] = "EC_WD_DISABLE"
       
  1416                         
       
  1417                         sync_managers.append(sync_manager_infos)
       
  1418                     
       
  1419                     pdos_index = []
       
  1420                     exclusive_pdos = {}
       
  1421                     selected_pdos = []
       
  1422                     for pdo, pdo_type in ([(pdo, "Inputs") for pdo in device.getTxPdo()] +
       
  1423                                           [(pdo, "Outputs") for pdo in device.getRxPdo()]):
       
  1424                         
       
  1425                         pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent())
       
  1426                         pdos_index.append(pdo_index)
       
  1427                         
       
  1428                         excluded_list = pdo.getExclude()
       
  1429                         if len(excluded_list) > 0:
       
  1430                             exclusion_list = [pdo_index]
       
  1431                             for excluded in excluded_list:
       
  1432                                 exclusion_list.append(ExtractHexDecValue(excluded.getcontent()))
       
  1433                             exclusion_list.sort()
       
  1434                             
       
  1435                             exclusion_scope = exclusive_pdos.setdefault(tuple(exclusion_list), [])
       
  1436                             
       
  1437                             entries = pdo.getEntry()
       
  1438                             pdo_mapping_match = {
       
  1439                                 "index": pdo_index, 
       
  1440                                 "matching": 0, 
       
  1441                                 "count": len(entries), 
       
  1442                                 "assigned": pdo.getSm() is not None
       
  1443                             }
       
  1444                             exclusion_scope.append(pdo_mapping_match)
       
  1445                             
       
  1446                             for entry in entries:
       
  1447                                 index = ExtractHexDecValue(entry.getIndex().getcontent())
       
  1448                                 subindex = ExtractHexDecValue(entry.getSubIndex())
       
  1449                                 if slave_variables.get((index, subindex), None) is not None:
       
  1450                                     pdo_mapping_match["matching"] += 1
       
  1451                         
       
  1452                         elif pdo.getMandatory():
       
  1453                             selected_pdos.append(pdo_index)
       
  1454                     
       
  1455                     excluded_pdos = []
       
  1456                     for exclusion_scope in exclusive_pdos.itervalues():
       
  1457                         exclusion_scope.sort(ExclusionSortFunction)
       
  1458                         start_excluding_index = 0
       
  1459                         if exclusion_scope[0]["matching"] > 0:
       
  1460                             selected_pdos.append(exclusion_scope[0]["index"])
       
  1461                             start_excluding_index = 1
       
  1462                         excluded_pdos.extend([pdo["index"] for pdo in exclusion_scope[start_excluding_index:] if PdoAssign or not pdo["assigned"]])
       
  1463                     
       
  1464                     for pdo, pdo_type in ([(pdo, "Inputs") for pdo in device.getTxPdo()] +
       
  1465                                           [(pdo, "Outputs") for pdo in device.getRxPdo()]):
       
  1466                         entries = pdo.getEntry()
       
  1467                         
       
  1468                         pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent())
       
  1469                         if pdo_index in excluded_pdos:
       
  1470                             continue
       
  1471                         
       
  1472                         pdo_needed = pdo_index in selected_pdos
       
  1473                         
       
  1474                         entries_infos = []
       
  1475                         
       
  1476                         for entry in entries:
       
  1477                             index = ExtractHexDecValue(entry.getIndex().getcontent())
       
  1478                             subindex = ExtractHexDecValue(entry.getSubIndex())
       
  1479                             entry_infos = {
       
  1480                                 "index": index,
       
  1481                                 "subindex": subindex,
       
  1482                                 "name": ExtractName(entry.getName()),
       
  1483                                 "bitlen": entry.getBitLen(),
       
  1484                             }
       
  1485                             entry_infos.update(type_infos)
       
  1486                             entries_infos.append("    {0x%(index).4x, 0x%(subindex).2x, %(bitlen)d}, /* %(name)s */" % entry_infos)
       
  1487                             
       
  1488                             entry_declaration = slave_variables.get((index, subindex), None)
       
  1489                             if entry_declaration is not None and not entry_declaration["mapped"]:
       
  1490                                 pdo_needed = True
       
  1491                                 
       
  1492                                 entry_infos.update(dict(zip(["var_type", "dir", "var_name", "no_decl", "extra_declarations"], 
       
  1493                                                             entry_declaration["infos"])))
       
  1494                                 entry_declaration["mapped"] = True
       
  1495                                 
       
  1496                                 entry_type = entry.getDataType().getcontent()
       
  1497                                 if entry_infos["var_type"] != entry_type:
       
  1498                                     message = _("Wrong type for location \"%s\"!") % entry_infos["var_name"]
       
  1499                                     if (self.Controler.GetSizeOfType(entry_infos["var_type"]) != 
       
  1500                                         self.Controler.GetSizeOfType(entry_type)):
       
  1501                                         raise ValueError, message
       
  1502                                     else:
       
  1503                                         self.Controler.GetCTRoot().logger.write_warning(_("Warning: ") + message + "\n")
       
  1504                                 
       
  1505                                 if (entry_infos["dir"] == "I" and pdo_type != "Inputs" or 
       
  1506                                     entry_infos["dir"] == "Q" and pdo_type != "Outputs"):
       
  1507                                     raise ValueError, _("Wrong direction for location \"%s\"!") % entry_infos["var_name"]
       
  1508                                 
       
  1509                                 ConfigureVariable(entry_infos, str_completion)
       
  1510                             
       
  1511                             elif pdo_type == "Outputs" and entry.getDataType() is not None and device_coe is not None:
       
  1512                                 data_type = entry.getDataType().getcontent()
       
  1513                                 entry_infos["dir"] = "Q"
       
  1514                                 entry_infos["data_size"] = max(1, entry_infos["bitlen"] / 8)
       
  1515                                 entry_infos["data_type"] = DATATYPECONVERSION.get(data_type)
       
  1516                                 entry_infos["var_type"] = data_type
       
  1517                                 entry_infos["real_var"] = "slave%(slave)d_%(index).4x_%(subindex).2x_default" % entry_infos
       
  1518                                 
       
  1519                                 ConfigureVariable(entry_infos, str_completion)
       
  1520                                 
       
  1521                                 str_completion["slaves_output_pdos_default_values_extraction"] += \
       
  1522                                     SLAVE_OUTPUT_PDO_DEFAULT_VALUE % entry_infos
       
  1523                                 
       
  1524                         if pdo_needed:
       
  1525                             for excluded in pdo.getExclude():
       
  1526                                 excluded_index = ExtractHexDecValue(excluded.getcontent())
       
  1527                                 if excluded_index not in excluded_pdos:
       
  1528                                     excluded_pdos.append(excluded_index)
       
  1529                             
       
  1530                             sm = pdo.getSm()
       
  1531                             if sm is None:
       
  1532                                 for sm_idx, sync_manager in enumerate(sync_managers):
       
  1533                                     if sync_manager["name"] == pdo_type:
       
  1534                                         sm = sm_idx
       
  1535                             if sm is None:
       
  1536                                 raise ValueError, _("No sync manager available for %s pdo!") % pdo_type
       
  1537                                 
       
  1538                             sync_managers[sm]["pdos_number"] += 1
       
  1539                             sync_managers[sm]["pdos"].append(
       
  1540                                 {"slave": slave_idx,
       
  1541                                  "index": pdo_index,
       
  1542                                  "name": ExtractName(pdo.getName()),
       
  1543                                  "type": pdo_type, 
       
  1544                                  "entries": entries_infos,
       
  1545                                  "entries_number": len(entries_infos),
       
  1546                                  "fixed": pdo.getFixed() == True})
       
  1547                 
       
  1548                     if PdoConfig and PdoAssign:
       
  1549                         dynamic_pdos = {}
       
  1550                         dynamic_pdos_number = 0
       
  1551                         for category, min_index, max_index in [("Inputs", 0x1600, 0x1800), 
       
  1552                                                                ("Outputs", 0x1a00, 0x1C00)]:
       
  1553                             for sync_manager in sync_managers:
       
  1554                                 if sync_manager["name"] == category:
       
  1555                                     category_infos = dynamic_pdos.setdefault(category, {})
       
  1556                                     category_infos["sync_manager"] = sync_manager
       
  1557                                     category_infos["pdos"] = [pdo for pdo in category_infos["sync_manager"]["pdos"] 
       
  1558                                                               if not pdo["fixed"] and pdo["type"] == category]
       
  1559                                     category_infos["current_index"] = min_index
       
  1560                                     category_infos["max_index"] = max_index
       
  1561                                     break
       
  1562                         
       
  1563                         for (index, subindex), entry_declaration in slave_variables.iteritems():
       
  1564                             
       
  1565                             if not entry_declaration["mapped"]:
       
  1566                                 entry = device_entries.get((index, subindex), None)
       
  1567                                 if entry is None:
       
  1568                                     raise ValueError, _("Unknown entry index 0x%4.4x, subindex 0x%2.2x for device %s") % \
       
  1569                                                      (index, subindex, type_infos["device_type"])
       
  1570                                 
       
  1571                                 entry_infos = {
       
  1572                                     "index": index,
       
  1573                                     "subindex": subindex,
       
  1574                                     "name": entry["Name"],
       
  1575                                     "bitlen": entry["BitSize"],
       
  1576                                 }
       
  1577                                 entry_infos.update(type_infos)
       
  1578                                 
       
  1579                                 entry_infos.update(dict(zip(["var_type", "dir", "var_name", "no_decl", "extra_declarations"], 
       
  1580                                                             entry_declaration["infos"])))
       
  1581                                 entry_declaration["mapped"] = True
       
  1582                                 
       
  1583                                 if entry_infos["var_type"] != entry["Type"]:
       
  1584                                     message = _("Wrong type for location \"%s\"!") % entry_infos["var_name"]
       
  1585                                     if (self.Controler.GetSizeOfType(entry_infos["var_type"]) != 
       
  1586                                         self.Controler.GetSizeOfType(entry["Type"])):
       
  1587                                         raise ValueError, message
       
  1588                                     else:
       
  1589                                         self.Controler.GetCTRoot().logger.write_warning(message + "\n")
       
  1590                                 
       
  1591                                 if entry_infos["dir"] == "I" and entry["PDOMapping"] in ["T", "RT"]:
       
  1592                                     pdo_type = "Inputs"
       
  1593                                 elif entry_infos["dir"] == "Q" and entry["PDOMapping"] in ["R", "RT"]:
       
  1594                                     pdo_type = "Outputs"
       
  1595                                 else:
       
  1596                                     raise ValueError, _("Wrong direction for location \"%s\"!") % entry_infos["var_name"]
       
  1597                                 
       
  1598                                 if not dynamic_pdos.has_key(pdo_type):
       
  1599                                     raise ValueError, _("No Sync manager defined for %s!") % pdo_type
       
  1600                                 
       
  1601                                 ConfigureVariable(entry_infos, str_completion)
       
  1602                                 
       
  1603                                 if len(dynamic_pdos[pdo_type]["pdos"]) > 0:
       
  1604                                     pdo = dynamic_pdos[pdo_type]["pdos"][0]
       
  1605                                 else:
       
  1606                                     while dynamic_pdos[pdo_type]["current_index"] in pdos_index:
       
  1607                                         dynamic_pdos[pdo_type]["current_index"] += 1
       
  1608                                     if dynamic_pdos[pdo_type]["current_index"] >= dynamic_pdos[pdo_type]["max_index"]:
       
  1609                                         raise ValueError, _("No more free PDO index available for %s!") % pdo_type
       
  1610                                     pdos_index.append(dynamic_pdos[pdo_type]["current_index"])
       
  1611                                     
       
  1612                                     dynamic_pdos_number += 1
       
  1613                                     pdo = {"slave": slave_idx,
       
  1614                                            "index": dynamic_pdos[pdo_type]["current_index"],
       
  1615                                            "name": "Dynamic PDO %d" % dynamic_pdos_number,
       
  1616                                            "type": pdo_type, 
       
  1617                                            "entries": [],
       
  1618                                            "entries_number": 0,
       
  1619                                            "fixed": False}
       
  1620                                     dynamic_pdos[pdo_type]["sync_manager"]["pdos_number"] += 1
       
  1621                                     dynamic_pdos[pdo_type]["sync_manager"]["pdos"].append(pdo)
       
  1622                                     dynamic_pdos[pdo_type]["pdos"].append(pdo)
       
  1623                                 
       
  1624                                 pdo["entries"].append("    {0x%(index).4x, 0x%(subindex).2x, %(bitlen)d}, /* %(name)s */" % entry_infos)
       
  1625                                 if entry_infos["bitlen"] < alignment:
       
  1626                                     print (alignment, entry_infos["bitlen"])
       
  1627                                     pdo["entries"].append("    {0x0000, 0x00, %d}, /* None */" % (alignment - entry_infos["bitlen"]))
       
  1628                                 pdo["entries_number"] += 1
       
  1629                                 
       
  1630                                 if pdo["entries_number"] == 255:
       
  1631                                     dynamic_pdos[pdo_type]["pdos"].pop(0)
       
  1632                     
       
  1633                     pdo_offset = 0
       
  1634                     entry_offset = 0
       
  1635                     for sync_manager_infos in sync_managers:
       
  1636                     
       
  1637                         for pdo_infos in sync_manager_infos["pdos"]:
       
  1638                             pdo_infos["offset"] = entry_offset
       
  1639                             pdo_entries = pdo_infos["entries"]
       
  1640                             pdos_infos["pdos_infos"].append(
       
  1641                                 ("    {0x%(index).4x, %(entries_number)d, " + 
       
  1642                                  "slave_%(slave)d_pdo_entries + %(offset)d}, /* %(name)s */") % pdo_infos)
       
  1643                             entry_offset += len(pdo_entries)
       
  1644                             pdos_infos["pdos_entries_infos"].extend(pdo_entries)
       
  1645                         
       
  1646                         sync_manager_infos["offset"] = pdo_offset
       
  1647                         pdo_offset_shift = sync_manager_infos["pdos_number"]
       
  1648                         pdos_infos["pdos_sync_infos"].append(
       
  1649                             ("    {%(index)d, %(sync_manager_type)s, %(pdos_number)d, " + 
       
  1650                              ("slave_%(slave)d_pdos + %(offset)d" if pdo_offset_shift else "NULL") +
       
  1651                              ", %(watchdog)s},") % sync_manager_infos)
       
  1652                         pdo_offset += pdo_offset_shift  
       
  1653                     
       
  1654                     for element in ["pdos_entries_infos", "pdos_infos", "pdos_sync_infos"]:
       
  1655                         pdos_infos[element] = "\n".join(pdos_infos[element])
       
  1656                     
       
  1657                     str_completion["pdos_configuration_declaration"] += SLAVE_PDOS_CONFIGURATION_DECLARATION % pdos_infos
       
  1658                 
       
  1659                 for (index, subindex), entry_declaration in slave_variables.iteritems():
       
  1660                     if not entry_declaration["mapped"]:
       
  1661                         message = _("Entry index 0x%4.4x, subindex 0x%2.2x not mapped for device %s") % \
       
  1662                                         (index, subindex, type_infos["device_type"])
       
  1663                         self.Controler.GetCTRoot().logger.write_warning(_("Warning: ") + message + "\n")
       
  1664                     
       
  1665         for element in ["used_pdo_entry_offset_variables_declaration", 
       
  1666                         "used_pdo_entry_configuration", 
       
  1667                         "located_variables_declaration", 
       
  1668                         "retrieve_variables", 
       
  1669                         "publish_variables"]:
       
  1670             str_completion[element] = "\n".join(str_completion[element])
       
  1671         
       
  1672         etherlabfile = open(filepath, 'w')
       
  1673         etherlabfile.write(plc_etherlab_code % str_completion)
       
  1674         etherlabfile.close()
       
  1675 
    15 
  1676 #--------------------------------------------------
    16 #--------------------------------------------------
  1677 #                 Ethercat ConfNode
    17 #                 Ethercat ConfNode
  1678 #--------------------------------------------------
    18 #--------------------------------------------------
  1679 
    19 
  1858     for item in group["children"]:
   198     for item in group["children"]:
  1859         if item["type"] == ETHERCAT_GROUP:
   199         if item["type"] == ETHERCAT_GROUP:
  1860             SortGroupItems(item)
   200             SortGroupItems(item)
  1861     group["children"].sort(GroupItemCompare)
   201     group["children"].sort(GroupItemCompare)
  1862 
   202 
  1863 def ExtractName(names, default=None):
       
  1864     if len(names) == 1:
       
  1865         return names[0].getcontent()
       
  1866     else:
       
  1867         for name in names:
       
  1868             if name.getLcId() == 1033:
       
  1869                 return name.getcontent()
       
  1870     return default
       
  1871 
       
  1872 def ExtractPdoInfos(pdo, pdo_type, entries, limits=None):
   203 def ExtractPdoInfos(pdo, pdo_type, entries, limits=None):
  1873     pdo_index = pdo.getIndex().getcontent()
   204     pdo_index = pdo.getIndex().getcontent()
  1874     pdo_name = ExtractName(pdo.getName())
   205     pdo_name = ExtractName(pdo.getName())
  1875     for pdo_entry in pdo.getEntry():
   206     for pdo_entry in pdo.getEntry():
  1876         entry_index = pdo_entry.getIndex().getcontent()
   207         entry_index = pdo_entry.getIndex().getcontent()