etherlab/EthercatMaster.py
changeset 2111 f2cffda17d00
child 2112 e88cd6ff885e
equal deleted inserted replaced
2110:e8c43f542eb1 2111:f2cffda17d00
       
     1 import os
       
     2 import cPickle
       
     3 from xml.dom import minidom
       
     4 
       
     5 import wx
       
     6 
       
     7 from xmlclass import *
       
     8 
       
     9 from PLCControler import UndoBuffer, LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
       
    10 from ConfigTreeNode import ConfigTreeNode
       
    11 from dialogs import BrowseValuesLibraryDialog
       
    12 from IDEFrame import TITLE, FILEMENU, PROJECTTREE
       
    13 
       
    14 from EthercatSlave import _EthercatSlaveCTN, ExtractHexDecValue, GenerateHexDecValue, TYPECONVERSION, VARCLASSCONVERSION
       
    15 from EthercatCFileGenerator import _EthercatCFileGenerator
       
    16 from ConfigEditor import MasterEditor
       
    17 from POULibrary import POULibrary
       
    18 
       
    19 try:
       
    20     from EthercatCIA402Slave import _EthercatCIA402SlaveCTN
       
    21     HAS_MCL = True
       
    22 except:
       
    23     HAS_MCL = False
       
    24 
       
    25 #--------------------------------------------------
       
    26 #         Remote Exec Etherlab Commands
       
    27 #--------------------------------------------------
       
    28 
       
    29 SCAN_COMMAND = """
       
    30 import commands
       
    31 result = commands.getoutput("ethercat slaves")
       
    32 slaves = []
       
    33 for slave_line in result.splitlines():
       
    34     chunks = slave_line.split()
       
    35     idx, pos, state, flag = chunks[:4]
       
    36     name = " ".join(chunks[4:])
       
    37     alias, position = pos.split(":")
       
    38     slave = {"idx": int(idx),
       
    39              "alias": int(alias),
       
    40              "position": int(position),
       
    41              "name": name}
       
    42     details = commands.getoutput("ethercat slaves -p %d -v" % slave["idx"])
       
    43     for details_line in details.splitlines():
       
    44         details_line = details_line.strip()
       
    45         for header, param in [("Vendor Id:", "vendor_id"),
       
    46                               ("Product code:", "product_code"),
       
    47                               ("Revision number:", "revision_number")]:
       
    48             if details_line.startswith(header):
       
    49                 slave[param] = details_line.split()[-1]
       
    50                 break
       
    51     slaves.append(slave)
       
    52 returnVal = slaves
       
    53 """
       
    54 
       
    55 #--------------------------------------------------
       
    56 #      Etherlab Specific Blocks Library
       
    57 #--------------------------------------------------
       
    58 
       
    59 def GetLocalPath(filename):
       
    60     return os.path.join(os.path.split(__file__)[0], filename)
       
    61 
       
    62 class EtherlabLibrary(POULibrary):
       
    63     def GetLibraryPath(self):
       
    64         return GetLocalPath("pous.xml")
       
    65 
       
    66     def Generate_C(self, buildpath, varlist, IECCFLAGS):
       
    67         etherlab_ext_file = open(GetLocalPath("etherlab_ext.c"), 'r')
       
    68         etherlab_ext_code = etherlab_ext_file.read()
       
    69         etherlab_ext_file.close()
       
    70         
       
    71         Gen_etherlabfile_path = os.path.join(buildpath, "etherlab_ext.c")
       
    72         ethelabfile = open(Gen_etherlabfile_path,'w')
       
    73         ethelabfile.write(etherlab_ext_code)
       
    74         ethelabfile.close()
       
    75         
       
    76         runtimefile_path = os.path.join(os.path.split(__file__)[0], "runtime_etherlab.py")
       
    77         return ((["etherlab_ext"], [(Gen_etherlabfile_path, IECCFLAGS)], True), "", 
       
    78                 ("runtime_etherlab.py", file(GetLocalPath("runtime_etherlab.py"))))
       
    79     
       
    80 #--------------------------------------------------
       
    81 #                 Ethercat MASTER
       
    82 #--------------------------------------------------
       
    83 
       
    84 EtherCATConfigClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATConfig.xsd")) 
       
    85 
       
    86 def sort_commands(x, y):
       
    87     if x["Index"] == y["Index"]:
       
    88         return cmp(x["Subindex"], y["Subindex"])
       
    89     return cmp(x["Index"], y["Index"])
       
    90 
       
    91 cls = EtherCATConfigClasses.get("Config_Slave", None)
       
    92 if cls:
       
    93     
       
    94     def getType(self):
       
    95         slave_info = self.getInfo()
       
    96         return {"device_type": slave_info.getName(),
       
    97                 "vendor": GenerateHexDecValue(slave_info.getVendorId()),
       
    98                 "product_code": GenerateHexDecValue(slave_info.getProductCode(), 16),
       
    99                 "revision_number": GenerateHexDecValue(slave_info.getRevisionNo(), 16)}
       
   100     setattr(cls, "getType", getType)
       
   101 
       
   102     def setType(self, type_infos):
       
   103         slave_info = self.getInfo()
       
   104         slave_info.setName(type_infos["device_type"])
       
   105         slave_info.setVendorId(ExtractHexDecValue(type_infos["vendor"]))
       
   106         slave_info.setProductCode(ExtractHexDecValue(type_infos["product_code"]))
       
   107         slave_info.setRevisionNo(ExtractHexDecValue(type_infos["revision_number"]))
       
   108     setattr(cls, "setType", setType)
       
   109     
       
   110     def getInitCmds(self, create_default=False):
       
   111         Mailbox = self.getMailbox()
       
   112         if Mailbox is None:
       
   113             if create_default:
       
   114                 self.addMailbox()
       
   115                 Mailbox = self.getMailbox()
       
   116             else:
       
   117                 return None
       
   118         CoE = Mailbox.getCoE()
       
   119         if CoE is None:
       
   120             if create_default:
       
   121                 Mailbox.addCoE()
       
   122                 CoE = Mailbox.getCoE()
       
   123             else:
       
   124                 return None
       
   125         InitCmds = CoE.getInitCmds()
       
   126         if InitCmds is None and create_default:
       
   127             CoE.addInitCmds()
       
   128             InitCmds = CoE.getInitCmds()
       
   129         return InitCmds
       
   130     setattr(cls, "getInitCmds", getInitCmds)
       
   131     
       
   132     def getStartupCommands(self):
       
   133         pos = self.getInfo().getPhysAddr()
       
   134         InitCmds = self.getInitCmds()
       
   135         if InitCmds is None:
       
   136             return []
       
   137         commands = []
       
   138         for idx, InitCmd in enumerate(InitCmds.getInitCmd()):
       
   139             comment = InitCmd.getComment()
       
   140             if comment is None:
       
   141                 comment = ""
       
   142             commands.append({
       
   143                 "command_idx": idx,
       
   144                 "Position": pos,
       
   145                 "Index": InitCmd.getIndex(),
       
   146                 "Subindex": InitCmd.getSubIndex(),
       
   147                 "Value": InitCmd.getData(),
       
   148                 "Description": comment})
       
   149         commands.sort(sort_commands)
       
   150         return commands
       
   151     setattr(cls, "getStartupCommands", getStartupCommands)
       
   152     
       
   153     def appendStartupCommand(self, command_infos):
       
   154         InitCmds = self.getInitCmds(True)
       
   155         command = EtherCATConfigClasses["InitCmds_InitCmd"]()
       
   156         command.setIndex(command_infos["Index"])
       
   157         command.setSubIndex(command_infos["Subindex"])
       
   158         command.setData(command_infos["Value"])
       
   159         command.setComment(command_infos["Description"])
       
   160         InitCmds.appendInitCmd(command)
       
   161         return len(InitCmds.getInitCmd()) - 1
       
   162     setattr(cls, "appendStartupCommand", appendStartupCommand)
       
   163     
       
   164     def setStartupCommand(self, command_infos):
       
   165         InitCmds = self.getInitCmds()
       
   166         if InitCmds is not None:
       
   167             commands = InitCmds.getInitCmd()
       
   168             if command_infos["command_idx"] < len(commands):
       
   169                 command = commands[command_infos["command_idx"]]
       
   170                 command.setIndex(command_infos["Index"])
       
   171                 command.setSubIndex(command_infos["Subindex"])
       
   172                 command.setData(command_infos["Value"])
       
   173                 command.setComment(command_infos["Description"])
       
   174     setattr(cls, "setStartupCommand", setStartupCommand)
       
   175     
       
   176     def removeStartupCommand(self, command_idx):
       
   177         InitCmds = self.getInitCmds()
       
   178         if InitCmds is not None:
       
   179             if command_idx < len(InitCmds.getInitCmd()):
       
   180                 InitCmds.removeInitCmd(command_idx)
       
   181     setattr(cls, "removeStartupCommand", removeStartupCommand)
       
   182 
       
   183 ProcessVariablesXSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
       
   184     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       
   185       <xsd:element name="ProcessVariables">
       
   186         <xsd:complexType>
       
   187           <xsd:sequence>
       
   188             <xsd:element name="variable" minOccurs="0" maxOccurs="unbounded">
       
   189               <xsd:complexType>
       
   190                 <xsd:sequence>
       
   191                   <xsd:element name="ReadFrom" type="LocationDesc" minOccurs="0"/>
       
   192                   <xsd:element name="WriteTo" type="LocationDesc" minOccurs="0"/>
       
   193                 </xsd:sequence>
       
   194                 <xsd:attribute name="Name" type="xsd:string" use="required"/>
       
   195                 <xsd:attribute name="Comment" type="xsd:string" use="required"/>
       
   196               </xsd:complexType>
       
   197             </xsd:element>
       
   198           </xsd:sequence>
       
   199         </xsd:complexType>
       
   200       </xsd:element>
       
   201       <xsd:complexType name="LocationDesc">
       
   202         <xsd:attribute name="Position" type="xsd:integer" use="required"/>
       
   203         <xsd:attribute name="Index" type="xsd:integer" use="required"/>
       
   204         <xsd:attribute name="SubIndex" type="xsd:integer" use="required"/>
       
   205       </xsd:complexType>
       
   206     </xsd:schema>
       
   207 """
       
   208 
       
   209 ProcessVariablesClasses = GenerateClassesFromXSDstring(ProcessVariablesXSD) 
       
   210 
       
   211 class _EthercatCTN:
       
   212     
       
   213     CTNChildrenTypes = [("EthercatSlave", _EthercatSlaveCTN, "Ethercat Slave")]
       
   214     if HAS_MCL:
       
   215         CTNChildrenTypes.append(("EthercatCIA402Slave", _EthercatCIA402SlaveCTN, "Ethercat CIA402 Slave"))
       
   216     EditorType = MasterEditor
       
   217     
       
   218     def __init__(self):
       
   219         config_filepath = self.ConfigFileName()
       
   220         config_is_saved = False
       
   221         self.Config = EtherCATConfigClasses["EtherCATConfig"]()
       
   222         if os.path.isfile(config_filepath):
       
   223             config_xmlfile = open(config_filepath, 'r')
       
   224             config_tree = minidom.parse(config_xmlfile)
       
   225             config_xmlfile.close()
       
   226             
       
   227             for child in config_tree.childNodes:
       
   228                 if child.nodeType == config_tree.ELEMENT_NODE and child.nodeName == "EtherCATConfig":
       
   229                     self.Config.loadXMLTree(child)
       
   230                     config_is_saved = True
       
   231         
       
   232         process_filepath = self.ProcessVariablesFileName()
       
   233         process_is_saved = False
       
   234         self.ProcessVariables = ProcessVariablesClasses["ProcessVariables"]()
       
   235         if os.path.isfile(process_filepath):
       
   236             process_xmlfile = open(process_filepath, 'r')
       
   237             process_tree = minidom.parse(process_xmlfile)
       
   238             process_xmlfile.close()
       
   239             
       
   240             for child in process_tree.childNodes:
       
   241                 if child.nodeType == process_tree.ELEMENT_NODE and child.nodeName == "ProcessVariables":
       
   242                     self.ProcessVariables.loadXMLTree(child)
       
   243                     process_is_saved = True
       
   244         
       
   245         if config_is_saved and process_is_saved:
       
   246             self.CreateBuffer(True)
       
   247         else:
       
   248             self.CreateBuffer(False)
       
   249             self.OnCTNSave()
       
   250     
       
   251     def GetContextualMenuItems(self):
       
   252         return [("Add Ethercat Slave", "Add Ethercat Slave to Master", self.OnAddEthercatSlave)]
       
   253     
       
   254     def OnAddEthercatSlave(self, event):
       
   255         app_frame = self.GetCTRoot().AppFrame
       
   256         dialog = BrowseValuesLibraryDialog(app_frame, 
       
   257             "Ethercat Slave Type", self.GetSlaveTypesLibrary())
       
   258         if dialog.ShowModal() == wx.ID_OK:
       
   259             type_infos = dialog.GetValueInfos()
       
   260             device, alignment = self.GetModuleInfos(type_infos)
       
   261             if device is not None:
       
   262                 if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers():
       
   263                     ConfNodeType = "EthercatCIA402Slave"
       
   264                 else:
       
   265                     ConfNodeType = "EthercatSlave"
       
   266                 new_child = self.CTNAddChild("%s_0" % ConfNodeType, ConfNodeType)
       
   267                 new_child.SetParamsAttribute("SlaveParams.Type", type_infos)
       
   268                 self.CTNRequestSave()
       
   269                 new_child._OpenView()
       
   270                 app_frame._Refresh(TITLE, FILEMENU, PROJECTTREE)
       
   271         dialog.Destroy()
       
   272     
       
   273     def ExtractHexDecValue(self, value):
       
   274         return ExtractHexDecValue(value)
       
   275 
       
   276     def GetSizeOfType(self, type):
       
   277         return TYPECONVERSION.get(self.GetCTRoot().GetBaseType(type), None)
       
   278 
       
   279     def ConfigFileName(self):
       
   280         return os.path.join(self.CTNPath(), "config.xml")
       
   281     
       
   282     def ProcessVariablesFileName(self):
       
   283         return os.path.join(self.CTNPath(), "process_variables.xml")
       
   284     
       
   285     def FilterSlave(self, slave, vendor=None, slave_pos=None, slave_profile=None):
       
   286         if slave_pos is not None and slave.getInfo().getPhysAddr() != slave_pos:
       
   287             return False
       
   288         type_infos = slave.getType()
       
   289         if vendor is not None and ExtractHexDecValue(type_infos["vendor"]) != vendor:
       
   290             return False
       
   291         device, alignment = self.GetModuleInfos(type_infos)
       
   292         if slave_profile is not None and slave_profile not in device.GetProfileNumbers():
       
   293             return False
       
   294         return True
       
   295 
       
   296     def GetSlaves(self, vendor=None, slave_pos=None, slave_profile=None):
       
   297         slaves = []
       
   298         for slave in self.Config.getConfig().getSlave():
       
   299             if self.FilterSlave(slave, vendor, slave_pos, slave_profile):
       
   300                 slaves.append(slave.getInfo().getPhysAddr())
       
   301         slaves.sort()
       
   302         return slaves
       
   303 
       
   304     def GetSlave(self, slave_pos):
       
   305         for slave in self.Config.getConfig().getSlave():
       
   306             slave_info = slave.getInfo()
       
   307             if slave_info.getPhysAddr() == slave_pos:
       
   308                 return slave
       
   309         return None
       
   310 
       
   311     def GetStartupCommands(self, vendor=None, slave_pos=None, slave_profile=None):
       
   312         commands = []
       
   313         for slave in self.Config.getConfig().getSlave():
       
   314             if self.FilterSlave(slave, vendor, slave_pos, slave_profile):
       
   315                 commands.append((slave.getInfo().getPhysAddr(), slave.getStartupCommands()))
       
   316         commands.sort()
       
   317         return reduce(lambda x, y: x + y[1], commands, [])
       
   318     
       
   319     def AppendStartupCommand(self, command_infos):
       
   320         slave = self.GetSlave(command_infos["Position"])
       
   321         if slave is not None:
       
   322             command_idx = slave.appendStartupCommand(command_infos)
       
   323             self.BufferModel()
       
   324             return command_idx
       
   325         return None
       
   326     
       
   327     def SetStartupCommandInfos(self, command_infos):
       
   328         slave = self.GetSlave(command_infos["Position"])
       
   329         if slave is not None:
       
   330             slave.setStartupCommand(command_infos)
       
   331             self.BufferModel()
       
   332     
       
   333     def RemoveStartupCommand(self, slave_pos, command_idx, buffer=True):
       
   334         slave = self.GetSlave(slave_pos)
       
   335         if slave is not None:
       
   336             slave.removeStartupCommand(command_idx)
       
   337             if buffer:
       
   338                 self.BufferModel()
       
   339     
       
   340     def SetProcessVariables(self, variables):
       
   341         vars = []
       
   342         for var in variables:
       
   343             variable = ProcessVariablesClasses["ProcessVariables_variable"]()
       
   344             variable.setName(var["Name"])
       
   345             variable.setComment(var["Description"])
       
   346             if var["ReadFrom"] != "":
       
   347                 position, index, subindex = var["ReadFrom"]
       
   348                 if variable.getReadFrom() is None:
       
   349                     variable.addReadFrom()
       
   350                 read_from = variable.getReadFrom()
       
   351                 read_from.setPosition(position)
       
   352                 read_from.setIndex(index)
       
   353                 read_from.setSubIndex(subindex)
       
   354             elif variable.getReadFrom() is not None:
       
   355                 variable.deleteReadFrom()
       
   356             if var["WriteTo"] != "":
       
   357                 position, index, subindex = var["WriteTo"]
       
   358                 if variable.getWriteTo() is None:
       
   359                     variable.addWriteTo()
       
   360                 write_to = variable.getWriteTo()
       
   361                 write_to.setPosition(position)
       
   362                 write_to.setIndex(index)
       
   363                 write_to.setSubIndex(subindex)
       
   364             elif variable.getWriteTo() is not None:
       
   365                 variable.deleteWriteTo()
       
   366             vars.append(variable)
       
   367         self.ProcessVariables.setvariable(vars)
       
   368         self.BufferModel()
       
   369         
       
   370     def GetProcessVariables(self):
       
   371         variables = []
       
   372         idx = 0
       
   373         for variable in self.ProcessVariables.getvariable():
       
   374             var = {"Name": variable.getName(),
       
   375                    "Number": idx,
       
   376                    "Description": variable.getComment()}
       
   377             read_from = variable.getReadFrom()
       
   378             if read_from is not None:
       
   379                 var["ReadFrom"] = (read_from.getPosition(),
       
   380                                    read_from.getIndex(),
       
   381                                    read_from.getSubIndex())
       
   382             else:
       
   383                 var["ReadFrom"] = ""
       
   384             write_to = variable.getWriteTo()
       
   385             if write_to is not None:
       
   386                 var["WriteTo"] = (write_to.getPosition(),
       
   387                                    write_to.getIndex(),
       
   388                                    write_to.getSubIndex())
       
   389             else:
       
   390                 var["WriteTo"] = ""
       
   391             variables.append(var)
       
   392             idx += 1
       
   393         return variables
       
   394     
       
   395     def _ScanNetwork(self):
       
   396         app_frame = self.GetCTRoot().AppFrame
       
   397         
       
   398         execute = True
       
   399         if len(self.Children) > 0:
       
   400             dialog = wx.MessageDialog(app_frame, 
       
   401                 _("The current network configuration will be deleted.\nDo you want to continue?"), 
       
   402                 _("Scan Network"), 
       
   403                 wx.YES_NO|wx.ICON_QUESTION)
       
   404             execute = dialog.ShowModal() == wx.ID_YES
       
   405             dialog.Destroy()
       
   406         
       
   407         if execute:
       
   408             error, returnVal = self.RemoteExec(SCAN_COMMAND, returnVal = None)
       
   409             if error != 0:
       
   410                 dialog = wx.MessageDialog(app_frame, returnVal, "Error", wx.OK|wx.ICON_ERROR)
       
   411                 dialog.ShowModal()
       
   412                 dialog.Destroy()
       
   413             elif returnVal is not None:
       
   414                 for child in self.IECSortedChildren():
       
   415                     self._doRemoveChild(child)
       
   416                 
       
   417                 for slave in returnVal:
       
   418                     type_infos = {
       
   419                         "vendor": slave["vendor_id"],
       
   420                         "product_code": slave["product_code"],
       
   421                         "revision_number":slave["revision_number"],
       
   422                     }
       
   423                     device, alignment = self.GetModuleInfos(type_infos)
       
   424                     if device is not None:
       
   425                         if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers():
       
   426                             CTNType = "EthercatCIA402Slave"
       
   427                         else:
       
   428                             CTNType = "EthercatSlave"
       
   429                         self.CTNAddChild("slave%s" % slave["idx"], CTNType, slave["idx"])
       
   430                         self.SetSlaveAlias(slave["idx"], slave["alias"])
       
   431                         type_infos["device_type"] = device.getType().getcontent()
       
   432                         self.SetSlaveType(slave["idx"], type_infos)
       
   433 
       
   434     def CTNAddChild(self, CTNName, CTNType, IEC_Channel=0):
       
   435         """
       
   436         Create the confnodes that may be added as child to this node self
       
   437         @param CTNType: string desining the confnode class name (get name from CTNChildrenTypes)
       
   438         @param CTNName: string for the name of the confnode instance
       
   439         """
       
   440         newConfNodeOpj = ConfigTreeNode.CTNAddChild(self, CTNName, CTNType, IEC_Channel)
       
   441         
       
   442         slave = self.GetSlave(newConfNodeOpj.BaseParams.getIEC_Channel())
       
   443         if slave is None:
       
   444             slave = EtherCATConfigClasses["Config_Slave"]()
       
   445             slave_infos = slave.getInfo()
       
   446             slave_infos.setName("undefined")
       
   447             slave_infos.setPhysAddr(newConfNodeOpj.BaseParams.getIEC_Channel())
       
   448             slave_infos.setAutoIncAddr(0)
       
   449             self.Config.getConfig().appendSlave(slave)
       
   450             self.BufferModel()
       
   451             self.OnCTNSave()
       
   452         
       
   453         return newConfNodeOpj
       
   454 
       
   455     def _doRemoveChild(self, CTNInstance):
       
   456         slave_pos = CTNInstance.GetSlavePos()
       
   457         config = self.Config.getConfig()
       
   458         for idx, slave in enumerate(config.getSlave()):
       
   459             slave_infos = slave.getInfo()
       
   460             if slave_infos.getPhysAddr() == slave_pos:
       
   461                 config.removeSlave(idx)
       
   462                 self.BufferModel()
       
   463                 self.OnCTNSave()
       
   464         ConfigTreeNode._doRemoveChild(self, CTNInstance)
       
   465 
       
   466     def SetSlavePosition(self, slave_pos, new_pos):
       
   467         slave = self.GetSlave(slave_pos)
       
   468         if slave is not None:
       
   469             slave_info = slave.getInfo()
       
   470             slave_info.setPhysAddr(new_pos)
       
   471             for variable in self.ProcessVariables.getvariable():
       
   472                 read_from = variable.getReadFrom()
       
   473                 if read_from is not None and read_from.getPosition() == slave_pos:
       
   474                     read_from.setPosition(new_pos)
       
   475                 write_to = variable.getWriteTo()
       
   476                 if write_to is not None and write_to.getPosition() == slave_pos:
       
   477                     write_to.setPosition(new_pos)
       
   478             self.CreateBuffer(True)
       
   479             self.OnCTNSave()
       
   480             if self._View is not None:
       
   481                 self._View.RefreshView()
       
   482                 self._View.RefreshBuffer()
       
   483     
       
   484     def GetSlaveAlias(self, slave_pos):
       
   485         slave = self.GetSlave(slave_pos)
       
   486         if slave is not None:
       
   487             slave_info = slave.getInfo()
       
   488             return slave_info.getAutoIncAddr()
       
   489         return None
       
   490     
       
   491     def SetSlaveAlias(self, slave_pos, alias):
       
   492         slave = self.GetSlave(slave_pos)
       
   493         if slave is not None:
       
   494             slave_info = slave.getInfo()
       
   495             slave_info.setAutoIncAddr(alias)
       
   496             self.BufferModel()
       
   497     
       
   498     def GetSlaveType(self, slave_pos):
       
   499         slave = self.GetSlave(slave_pos)
       
   500         if slave is not None:
       
   501             return slave.getType()
       
   502         return None
       
   503     
       
   504     def SetSlaveType(self, slave_pos, type_infos):
       
   505         slave = self.GetSlave(slave_pos)
       
   506         if slave is not None:
       
   507             slave.setType(type_infos)
       
   508             self.BufferModel()
       
   509     
       
   510     def GetSlaveInfos(self, slave_pos):
       
   511         slave = self.GetSlave(slave_pos)
       
   512         if slave is not None:
       
   513             type_infos = slave.getType()
       
   514             device, alignment = self.GetModuleInfos(type_infos)
       
   515             if device is not None:
       
   516                 infos = type_infos.copy()
       
   517                 infos.update({"physics": device.getPhysics(),
       
   518                               "sync_managers": device.GetSyncManagers(),
       
   519                               "entries": self.GetSlaveVariables(device)})
       
   520                 return infos
       
   521         return None
       
   522     
       
   523     def GetSlaveVariables(self, slave_pos=None, limits=None, device=None):
       
   524         if device is None and slave_pos is not None:
       
   525             slave = self.GetSlave(slave_pos)
       
   526             if slave is not None:
       
   527                 type_infos = slave.getType()
       
   528                 device, alignment = self.GetModuleInfos(type_infos)
       
   529         if device is not None:
       
   530             entries = device.GetEntriesList(limits)
       
   531             entries_list = entries.items()
       
   532             entries_list.sort()
       
   533             entries = []
       
   534             current_index = None
       
   535             current_entry = None
       
   536             for (index, subindex), entry in entries_list:
       
   537                 entry["children"] = []
       
   538                 if slave_pos is not None:
       
   539                     entry["Position"] = str(slave_pos)
       
   540                 entry
       
   541                 if index != current_index:
       
   542                     current_index = index
       
   543                     current_entry = entry
       
   544                     entries.append(entry)
       
   545                 elif current_entry is not None:
       
   546                     current_entry["children"].append(entry)
       
   547                 else:
       
   548                     entries.append(entry)
       
   549             return entries
       
   550         return []
       
   551     
       
   552     def GetSlaveVariableDataType(self, slave_pos, index, subindex):
       
   553         slave = self.GetSlave(slave_pos)
       
   554         if slave is not None:
       
   555             device, alignment = self.GetModuleInfos(slave.getType())
       
   556             if device is not None:
       
   557                 entries = device.GetEntriesList()
       
   558                 entry_infos = entries.get((index, subindex))
       
   559                 if entry_infos is not None:
       
   560                     return entry_infos["Type"]
       
   561         return None
       
   562     
       
   563     def GetNodesVariables(self, vendor=None, slave_pos=None, slave_profile=None, limits=None):
       
   564         entries = []
       
   565         for slave_position in self.GetSlaves():
       
   566             if slave_pos is not None and slave_position != slave_pos:
       
   567                 continue
       
   568             slave = self.GetSlave(slave_position)
       
   569             type_infos = slave.getType()
       
   570             if vendor is not None and ExtractHexDecValue(type_infos["vendor"]) != vendor:
       
   571                 continue
       
   572             device, alignment = self.GetModuleInfos(type_infos)
       
   573             if slave_profile is not None and slave_profile not in device.GetProfileNumbers():
       
   574                 continue
       
   575             entries.extend(self.GetSlaveVariables(slave_position, limits, device))
       
   576         return entries
       
   577      
       
   578     def GetModuleInfos(self, type_infos):
       
   579         return self.CTNParent.GetModuleInfos(type_infos)
       
   580     
       
   581     def GetSlaveTypesLibrary(self, profile_filter=None):
       
   582         return self.CTNParent.GetModulesLibrary(profile_filter)
       
   583     
       
   584     def GetLibraryVendors(self):
       
   585         return self.CTNParent.GetVendors()
       
   586     
       
   587     def GetDeviceLocationTree(self, slave_pos, current_location, device_name):
       
   588         slave = self.GetSlave(slave_pos)
       
   589         vars = []    
       
   590         if slave is not None:
       
   591             type_infos = slave.getType()
       
   592         
       
   593             device, alignment = self.GetModuleInfos(type_infos)
       
   594             if device is not None:
       
   595                 sync_managers = []
       
   596                 for sync_manager in device.getSm():
       
   597                     sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte())
       
   598                     sync_manager_direction = sync_manager_control_byte & 0x0c
       
   599                     if sync_manager_direction:
       
   600                         sync_managers.append(LOCATION_VAR_OUTPUT)
       
   601                     else:
       
   602                         sync_managers.append(LOCATION_VAR_INPUT)
       
   603                 
       
   604                 entries = device.GetEntriesList().items()
       
   605                 entries.sort()
       
   606                 for (index, subindex), entry in entries:
       
   607                     var_size = self.GetSizeOfType(entry["Type"])
       
   608                     if var_size is not None:
       
   609                         var_class = VARCLASSCONVERSION.get(entry["PDOMapping"], None)
       
   610                         if var_class is not None:
       
   611                             if var_class == LOCATION_VAR_INPUT:
       
   612                                 var_dir = "%I"
       
   613                             else:
       
   614                                 var_dir = "%Q"    
       
   615                         
       
   616                             vars.append({"name": "0x%4.4x-0x%2.2x: %s" % (index, subindex, entry["Name"]),
       
   617                                          "type": var_class,
       
   618                                          "size": var_size,
       
   619                                          "IEC_type": entry["Type"],
       
   620                                          "var_name": "%s_%4.4x_%2.2x" % ("_".join(device_name.split()), index, subindex),
       
   621                                          "location": "%s%s%s"%(var_dir, var_size, ".".join(map(str, current_location + 
       
   622                                                                                                     (index, subindex)))),
       
   623                                          "description": "",
       
   624                                          "children": []})
       
   625         
       
   626         return vars
       
   627     
       
   628     def CTNTestModified(self):
       
   629         return self.ChangesToSave or not self.ModelIsSaved()    
       
   630 
       
   631     def OnCTNSave(self):
       
   632         config_filepath = self.ConfigFileName()
       
   633         
       
   634         config_text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
       
   635         config_extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",
       
   636                   "xsi:noNamespaceSchemaLocation" : "EtherCATInfo.xsd"}
       
   637         config_text += self.Config.generateXMLText("EtherCATConfig", 0, config_extras)
       
   638 
       
   639         config_xmlfile = open(config_filepath,"w")
       
   640         config_xmlfile.write(config_text.encode("utf-8"))
       
   641         config_xmlfile.close()
       
   642         
       
   643         process_filepath = self.ProcessVariablesFileName()
       
   644         
       
   645         process_text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
       
   646         process_extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance"}
       
   647         process_text += self.ProcessVariables.generateXMLText("ProcessVariables", 0, process_extras)
       
   648 
       
   649         process_xmlfile = open(process_filepath,"w")
       
   650         process_xmlfile.write(process_text.encode("utf-8"))
       
   651         process_xmlfile.close()
       
   652         
       
   653         self.Buffer.CurrentSaved()
       
   654         return True
       
   655 
       
   656     def GetProcessVariableName(self, location, var_type):
       
   657         return "__M%s_%s" % (self.GetSizeOfType(var_type), "_".join(map(str, location)))
       
   658 
       
   659     def _Generate_C(self, buildpath, locations):
       
   660         current_location = self.GetCurrentLocation()
       
   661         # define a unique name for the generated C file
       
   662         location_str = "_".join(map(lambda x:str(x), current_location))
       
   663         
       
   664         Gen_Ethercatfile_path = os.path.join(buildpath, "ethercat_%s.c"%location_str)
       
   665         
       
   666         self.FileGenerator = _EthercatCFileGenerator(self)
       
   667         
       
   668         LocationCFilesAndCFLAGS, LDFLAGS, extra_files = ConfigTreeNode._Generate_C(self, buildpath, locations)
       
   669         
       
   670         for idx, variable in enumerate(self.ProcessVariables.getvariable()):
       
   671             name = None
       
   672             var_type = None
       
   673             read_from = variable.getReadFrom()
       
   674             write_to = variable.getWriteTo()
       
   675             if read_from is not None:
       
   676                 pos = read_from.getPosition()
       
   677                 index = read_from.getIndex()
       
   678                 subindex = read_from.getSubIndex()
       
   679                 location = current_location + (idx, )
       
   680                 var_type = self.GetSlaveVariableDataType(pos, index, subindex)
       
   681                 name = self.FileGenerator.DeclareVariable(
       
   682                             pos, index, subindex, var_type, "I",
       
   683                             self.GetProcessVariableName(location, var_type))
       
   684             if write_to is not None:
       
   685                 pos = write_to.getPosition()
       
   686                 index = write_to.getIndex()
       
   687                 subindex = write_to.getSubIndex()
       
   688                 if name is None:
       
   689                     location = current_location + (idx, )
       
   690                     var_type = self.GetSlaveVariableDataType(pos, index, subindex)
       
   691                     name = self.GetProcessVariableName(location, var_type)
       
   692                 self.FileGenerator.DeclareVariable(
       
   693                             pos, index, subindex, var_type, "Q", name, True)
       
   694         
       
   695         self.FileGenerator.GenerateCFile(Gen_Ethercatfile_path, location_str, self.BaseParams.getIEC_Channel())
       
   696         
       
   697         LocationCFilesAndCFLAGS.append(
       
   698             (current_location, 
       
   699              [(Gen_Ethercatfile_path, '"-I%s"'%os.path.abspath(self.GetCTRoot().GetIECLibPath()))], 
       
   700              True))
       
   701         LDFLAGS.append("-lethercat -lrtdm")
       
   702         
       
   703         return LocationCFilesAndCFLAGS, LDFLAGS, extra_files
       
   704 
       
   705     ConfNodeMethods = [
       
   706         {"bitmap" : "ScanNetwork",
       
   707          "name" : _("Scan Network"), 
       
   708          "tooltip" : _("Scan Network"),
       
   709          "method" : "_ScanNetwork"},
       
   710     ]
       
   711 
       
   712     def CTNGenerate_C(self, buildpath, locations):
       
   713         current_location = self.GetCurrentLocation()
       
   714         
       
   715         slaves = self.GetSlaves()
       
   716         for slave_pos in slaves:
       
   717             slave = self.GetSlave(slave_pos)
       
   718             if slave is not None:
       
   719                 self.FileGenerator.DeclareSlave(slave_pos, slave)
       
   720         
       
   721         for location in locations:
       
   722             loc = location["LOC"][len(current_location):]
       
   723             slave_pos = loc[0]
       
   724             if slave_pos in slaves and len(loc) == 3 and location["DIR"] != "M":
       
   725                 self.FileGenerator.DeclareVariable(
       
   726                     slave_pos, loc[1], loc[2], location["IEC_TYPE"], location["DIR"], location["NAME"])
       
   727         
       
   728         return [],"",False
       
   729         
       
   730 #-------------------------------------------------------------------------------
       
   731 #                      Current Buffering Management Functions
       
   732 #-------------------------------------------------------------------------------
       
   733 
       
   734     """
       
   735     Return a copy of the config
       
   736     """
       
   737     def Copy(self, model):
       
   738         return cPickle.loads(cPickle.dumps(model))
       
   739     
       
   740     def CreateBuffer(self, saved):
       
   741         self.Buffer = UndoBuffer(cPickle.dumps((self.Config, self.ProcessVariables)), saved)
       
   742         
       
   743     def BufferModel(self):
       
   744         self.Buffer.Buffering(cPickle.dumps((self.Config, self.ProcessVariables)))
       
   745     
       
   746     def ModelIsSaved(self):
       
   747         if self.Buffer is not None:
       
   748             return self.Buffer.IsCurrentSaved()
       
   749         else:
       
   750             return True
       
   751 
       
   752     def LoadPrevious(self):
       
   753         self.Config, self.ProcessVariables = cPickle.loads(self.Buffer.Previous())
       
   754     
       
   755     def LoadNext(self):
       
   756         self.Config, self.ProcessVariables = cPickle.loads(self.Buffer.Next())
       
   757     
       
   758     def GetBufferState(self):
       
   759         first = self.Buffer.IsFirst()
       
   760         last = self.Buffer.IsLast()
       
   761         return not first, not last
       
   762