edouard@2165: #!/usr/bin/env python
edouard@2165: # -*- coding: utf-8 -*-
edouard@2165: 
edouard@2165: # This file is part of Beremiz
edouard@2165: #
edouard@2165: # Copyright (C) 2011-2014: Laurent BESSARD, Edouard TISSERANT
edouard@2165: #                          RTES Lab : CRKim, JBLee, youcu
edouard@2165: #                          Higen Motor : Donggu Kang
edouard@2165: #
edouard@2165: # See COPYING file for copyrights details.
edouard@2165: 
andrej@2405: from __future__ import absolute_import
Laurent@2111: import os
andrej@2390: from copy import deepcopy
Laurent@2157: from lxml import etree
Laurent@2111: 
Laurent@2111: import wx
Laurent@2111: 
Laurent@2111: from xmlclass import *
Laurent@2111: 
andrej@2396: from PLCControler import UndoBuffer, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT
Laurent@2111: from ConfigTreeNode import ConfigTreeNode
Laurent@2111: from dialogs import BrowseValuesLibraryDialog
Laurent@2111: from IDEFrame import TITLE, FILEMENU, PROJECTTREE
andrej@2392: from POULibrary import POULibrary
andrej@2392: 
andrej@2405: from etherlab.ConfigEditor import MasterEditor
andrej@2405: from etherlab.EthercatCFileGenerator import _EthercatCFileGenerator
andrej@2405: from etherlab.EthercatSlave import \
andrej@2405:     _EthercatSlaveCTN, \
andrej@2405:     ExtractHexDecValue, \
andrej@2405:     GenerateHexDecValue, \
andrej@2405:     TYPECONVERSION, \
andrej@2405:     VARCLASSCONVERSION, \
andrej@2405:     _CommonSlave
Laurent@2111: 
Laurent@2111: try:
andrej@2405:     from etherlab.EthercatCIA402Slave import _EthercatCIA402SlaveCTN
Laurent@2111:     HAS_MCL = True
andrej@2353: except Exception:
Laurent@2111:     HAS_MCL = False
Laurent@2111: 
andrej@2356: # --------------------------------------------------
Laurent@2111: #         Remote Exec Etherlab Commands
andrej@2356: # --------------------------------------------------
Laurent@2111: 
Laurent@2111: SCAN_COMMAND = """
Laurent@2111: import commands
Laurent@2111: result = commands.getoutput("ethercat slaves")
Laurent@2111: slaves = []
Laurent@2111: for slave_line in result.splitlines():
Laurent@2111:     chunks = slave_line.split()
Laurent@2111:     idx, pos, state, flag = chunks[:4]
Laurent@2111:     name = " ".join(chunks[4:])
Laurent@2111:     alias, position = pos.split(":")
Laurent@2111:     slave = {"idx": int(idx),
Laurent@2111:              "alias": int(alias),
Laurent@2111:              "position": int(position),
Laurent@2111:              "name": name}
Laurent@2111:     details = commands.getoutput("ethercat slaves -p %d -v" % slave["idx"])
Laurent@2111:     for details_line in details.splitlines():
Laurent@2111:         details_line = details_line.strip()
Laurent@2111:         for header, param in [("Vendor Id:", "vendor_id"),
Laurent@2111:                               ("Product code:", "product_code"),
Laurent@2111:                               ("Revision number:", "revision_number")]:
Laurent@2111:             if details_line.startswith(header):
Laurent@2111:                 slave[param] = details_line.split()[-1]
Laurent@2111:                 break
Laurent@2111:     slaves.append(slave)
Laurent@2111: returnVal = slaves
Laurent@2111: """
Laurent@2111: 
andrej@2356: # --------------------------------------------------
Laurent@2111: #      Etherlab Specific Blocks Library
andrej@2356: # --------------------------------------------------
Laurent@2111: 
andrej@2360: 
Laurent@2111: def GetLocalPath(filename):
Laurent@2111:     return os.path.join(os.path.split(__file__)[0], filename)
Laurent@2111: 
andrej@2360: 
Laurent@2111: class EtherlabLibrary(POULibrary):
Laurent@2111:     def GetLibraryPath(self):
Laurent@2111:         return GetLocalPath("pous.xml")
Laurent@2111: 
Laurent@2111:     def Generate_C(self, buildpath, varlist, IECCFLAGS):
Laurent@2111:         etherlab_ext_file = open(GetLocalPath("etherlab_ext.c"), 'r')
Laurent@2111:         etherlab_ext_code = etherlab_ext_file.read()
Laurent@2111:         etherlab_ext_file.close()
andrej@2355: 
Laurent@2111:         Gen_etherlabfile_path = os.path.join(buildpath, "etherlab_ext.c")
andrej@2363:         ethelabfile = open(Gen_etherlabfile_path, 'w')
Laurent@2111:         ethelabfile.write(etherlab_ext_code)
Laurent@2111:         ethelabfile.close()
andrej@2355: 
andrej@2355:         return ((["etherlab_ext"], [(Gen_etherlabfile_path, IECCFLAGS)], True), "",
andrej@2442:                 ("runtime_etherlab.py", open(GetLocalPath("runtime_etherlab.py"))))
andrej@2355: 
andrej@2356: # --------------------------------------------------
Laurent@2111: #                 Ethercat MASTER
andrej@2356: # --------------------------------------------------
Laurent@2111: 
andrej@2370: 
andrej@2355: EtherCATConfigParser = GenerateParserFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATConfig.xsd"))
Laurent@2111: 
andrej@2360: 
Laurent@2111: def sort_commands(x, y):
Laurent@2111:     if x["Index"] == y["Index"]:
Laurent@2111:         return cmp(x["Subindex"], y["Subindex"])
Laurent@2111:     return cmp(x["Index"], y["Index"])
Laurent@2111: 
andrej@2370: 
Laurent@2157: cls = EtherCATConfigParser.GetElementClass("Slave", "Config")
Laurent@2111: if cls:
andrej@2355: 
Laurent@2111:     def getType(self):
Laurent@2111:         slave_info = self.getInfo()
Laurent@2111:         return {"device_type": slave_info.getName(),
Laurent@2111:                 "vendor": GenerateHexDecValue(slave_info.getVendorId()),
Laurent@2111:                 "product_code": GenerateHexDecValue(slave_info.getProductCode(), 16),
Laurent@2111:                 "revision_number": GenerateHexDecValue(slave_info.getRevisionNo(), 16)}
Laurent@2111:     setattr(cls, "getType", getType)
Laurent@2111: 
Laurent@2111:     def setType(self, type_infos):
Laurent@2111:         slave_info = self.getInfo()
Laurent@2111:         slave_info.setName(type_infos["device_type"])
Laurent@2111:         slave_info.setVendorId(ExtractHexDecValue(type_infos["vendor"]))
Laurent@2111:         slave_info.setProductCode(ExtractHexDecValue(type_infos["product_code"]))
Laurent@2111:         slave_info.setRevisionNo(ExtractHexDecValue(type_infos["revision_number"]))
Laurent@2111:     setattr(cls, "setType", setType)
andrej@2355: 
Laurent@2111:     def getInitCmds(self, create_default=False):
Laurent@2111:         Mailbox = self.getMailbox()
Laurent@2111:         if Mailbox is None:
Laurent@2111:             if create_default:
Laurent@2111:                 self.addMailbox()
Laurent@2111:                 Mailbox = self.getMailbox()
Laurent@2111:             else:
Laurent@2111:                 return None
Laurent@2111:         CoE = Mailbox.getCoE()
Laurent@2111:         if CoE is None:
Laurent@2111:             if create_default:
Laurent@2111:                 Mailbox.addCoE()
Laurent@2111:                 CoE = Mailbox.getCoE()
Laurent@2111:             else:
Laurent@2111:                 return None
Laurent@2111:         InitCmds = CoE.getInitCmds()
Laurent@2111:         if InitCmds is None and create_default:
Laurent@2111:             CoE.addInitCmds()
Laurent@2111:             InitCmds = CoE.getInitCmds()
Laurent@2111:         return InitCmds
Laurent@2111:     setattr(cls, "getInitCmds", getInitCmds)
andrej@2355: 
Laurent@2111:     def getStartupCommands(self):
Laurent@2111:         pos = self.getInfo().getPhysAddr()
Laurent@2111:         InitCmds = self.getInitCmds()
Laurent@2111:         if InitCmds is None:
Laurent@2111:             return []
Laurent@2111:         commands = []
Laurent@2111:         for idx, InitCmd in enumerate(InitCmds.getInitCmd()):
Laurent@2111:             comment = InitCmd.getComment()
Laurent@2111:             if comment is None:
Laurent@2111:                 comment = ""
Laurent@2111:             commands.append({
Laurent@2111:                 "command_idx": idx,
Laurent@2111:                 "Position": pos,
Laurent@2111:                 "Index": InitCmd.getIndex(),
Laurent@2111:                 "Subindex": InitCmd.getSubIndex(),
Laurent@2111:                 "Value": InitCmd.getData(),
Laurent@2111:                 "Description": comment})
Laurent@2111:         commands.sort(sort_commands)
Laurent@2111:         return commands
Laurent@2111:     setattr(cls, "getStartupCommands", getStartupCommands)
andrej@2355: 
Laurent@2111:     def appendStartupCommand(self, command_infos):
Laurent@2111:         InitCmds = self.getInitCmds(True)
Laurent@2157:         command = EtherCATConfigParser.CreateElement("InitCmd", "InitCmds", 1)
Laurent@2157:         InitCmds.appendInitCmd(command)
Laurent@2111:         command.setIndex(command_infos["Index"])
Laurent@2111:         command.setSubIndex(command_infos["Subindex"])
Laurent@2111:         command.setData(command_infos["Value"])
Laurent@2111:         command.setComment(command_infos["Description"])
Laurent@2111:         return len(InitCmds.getInitCmd()) - 1
Laurent@2111:     setattr(cls, "appendStartupCommand", appendStartupCommand)
andrej@2355: 
Laurent@2111:     def setStartupCommand(self, command_infos):
Laurent@2111:         InitCmds = self.getInitCmds()
Laurent@2111:         if InitCmds is not None:
Laurent@2111:             commands = InitCmds.getInitCmd()
Laurent@2111:             if command_infos["command_idx"] < len(commands):
Laurent@2111:                 command = commands[command_infos["command_idx"]]
Laurent@2111:                 command.setIndex(command_infos["Index"])
Laurent@2111:                 command.setSubIndex(command_infos["Subindex"])
Laurent@2111:                 command.setData(command_infos["Value"])
Laurent@2111:                 command.setComment(command_infos["Description"])
Laurent@2111:     setattr(cls, "setStartupCommand", setStartupCommand)
andrej@2355: 
Laurent@2111:     def removeStartupCommand(self, command_idx):
Laurent@2111:         InitCmds = self.getInitCmds()
Laurent@2111:         if InitCmds is not None:
Laurent@2111:             if command_idx < len(InitCmds.getInitCmd()):
Laurent@2111:                 InitCmds.removeInitCmd(command_idx)
Laurent@2111:     setattr(cls, "removeStartupCommand", removeStartupCommand)
Laurent@2111: 
Laurent@2111: ProcessVariablesXSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
Laurent@2111:     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
Laurent@2111:       <xsd:element name="ProcessVariables">
Laurent@2111:         <xsd:complexType>
Laurent@2111:           <xsd:sequence>
Laurent@2111:             <xsd:element name="variable" minOccurs="0" maxOccurs="unbounded">
Laurent@2111:               <xsd:complexType>
Laurent@2111:                 <xsd:sequence>
Laurent@2111:                   <xsd:element name="ReadFrom" type="LocationDesc" minOccurs="0"/>
Laurent@2111:                   <xsd:element name="WriteTo" type="LocationDesc" minOccurs="0"/>
Laurent@2111:                 </xsd:sequence>
Laurent@2111:                 <xsd:attribute name="Name" type="xsd:string" use="required"/>
Laurent@2111:                 <xsd:attribute name="Comment" type="xsd:string" use="required"/>
Laurent@2111:               </xsd:complexType>
Laurent@2111:             </xsd:element>
Laurent@2111:           </xsd:sequence>
Laurent@2111:         </xsd:complexType>
Laurent@2111:       </xsd:element>
Laurent@2111:       <xsd:complexType name="LocationDesc">
Laurent@2111:         <xsd:attribute name="Position" type="xsd:integer" use="required"/>
Laurent@2111:         <xsd:attribute name="Index" type="xsd:integer" use="required"/>
Laurent@2111:         <xsd:attribute name="SubIndex" type="xsd:integer" use="required"/>
Laurent@2111:       </xsd:complexType>
Laurent@2111:     </xsd:schema>
Laurent@2111: """
Laurent@2111: 
andrej@2355: ProcessVariablesParser = GenerateParserFromXSDstring(ProcessVariablesXSD)
Laurent@2111: 
andrej@2360: 
andrej@2397: class _EthercatCTN(object):
Edouard@2152: 
Laurent@2111:     CTNChildrenTypes = [("EthercatSlave", _EthercatSlaveCTN, "Ethercat Slave")]
Laurent@2111:     if HAS_MCL:
Laurent@2111:         CTNChildrenTypes.append(("EthercatCIA402Slave", _EthercatCIA402SlaveCTN, "Ethercat CIA402 Slave"))
Laurent@2111:     EditorType = MasterEditor
andrej@2355: 
Laurent@2111:     def __init__(self):
Laurent@2111:         config_filepath = self.ConfigFileName()
Laurent@2111:         config_is_saved = False
Laurent@2160:         self.Config = None
Laurent@2111:         if os.path.isfile(config_filepath):
Laurent@2111:             config_xmlfile = open(config_filepath, 'r')
Laurent@2160:             try:
Laurent@2160:                 self.Config, error = \
Laurent@2160:                     EtherCATConfigParser.LoadXMLString(config_xmlfile.read())
Laurent@2160:                 if error is None:
Laurent@2160:                     config_is_saved = True
andrej@2418:             except Exception as e:
andrej@2447:                 error = str(e)
Laurent@2111:             config_xmlfile.close()
andrej@2355: 
Laurent@2160:             if error is not None:
Laurent@2160:                 self.GetCTRoot().logger.write_error(
andrej@2404:                     _("Couldn't load %s network configuration file.") % self.CTNName())
andrej@2355: 
Laurent@2160:         if self.Config is None:
Laurent@2157:             self.Config = EtherCATConfigParser.CreateElement("EtherCATConfig")
andrej@2355: 
Laurent@2111:         process_filepath = self.ProcessVariablesFileName()
Laurent@2111:         process_is_saved = False
Laurent@2160:         self.ProcessVariables = None
Laurent@2111:         if os.path.isfile(process_filepath):
Laurent@2111:             process_xmlfile = open(process_filepath, 'r')
Laurent@2160:             try:
Laurent@2160:                 self.ProcessVariables, error = \
Laurent@2160:                     ProcessVariablesParser.LoadXMLString(process_xmlfile.read())
Laurent@2160:                 if error is None:
Laurent@2160:                     process_is_saved = True
andrej@2418:             except Exception as e:
andrej@2447:                 error = str(e)
Laurent@2111:             process_xmlfile.close()
andrej@2355: 
Laurent@2160:             if error is not None:
Laurent@2160:                 self.GetCTRoot().logger.write_error(
andrej@2404:                     _("Couldn't load %s network process variables file.") % self.CTNName())
andrej@2355: 
Laurent@2160:         if self.ProcessVariables is None:
Laurent@2157:             self.ProcessVariables = ProcessVariablesParser.CreateElement("ProcessVariables")
andrej@2355: 
Laurent@2111:         if config_is_saved and process_is_saved:
Laurent@2111:             self.CreateBuffer(True)
Laurent@2111:         else:
Laurent@2111:             self.CreateBuffer(False)
Laurent@2111:             self.OnCTNSave()
andrej@2355: 
Edouard@2152:         # ----------- call ethercat mng. function --------------
Edouard@2152:         self.CommonMethod = _CommonSlave(self)
andrej@2355: 
Laurent@2149:     def GetIconName(self):
Laurent@2149:         return "Ethercat"
andrej@2355: 
Laurent@2111:     def GetContextualMenuItems(self):
andrej@2423:         return [(_("Add Ethercat Slave"), _("Add Ethercat Slave to Master"), self.OnAddEthercatSlave)]
andrej@2355: 
Laurent@2111:     def OnAddEthercatSlave(self, event):
Laurent@2111:         app_frame = self.GetCTRoot().AppFrame
andrej@2355:         dialog = BrowseValuesLibraryDialog(app_frame,
andrej@2423:                                            _("Ethercat Slave Type"),
andrej@2381:                                            self.GetSlaveTypesLibrary())
Laurent@2111:         if dialog.ShowModal() == wx.ID_OK:
Laurent@2111:             type_infos = dialog.GetValueInfos()
andrej@2406:             device, _module_extra_params = self.GetModuleInfos(type_infos)
Laurent@2111:             if device is not None:
Laurent@2111:                 if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers():
Laurent@2111:                     ConfNodeType = "EthercatCIA402Slave"
Laurent@2111:                 else:
Laurent@2111:                     ConfNodeType = "EthercatSlave"
Laurent@2111:                 new_child = self.CTNAddChild("%s_0" % ConfNodeType, ConfNodeType)
Laurent@2111:                 new_child.SetParamsAttribute("SlaveParams.Type", type_infos)
Laurent@2111:                 self.CTNRequestSave()
Laurent@2111:                 new_child._OpenView()
Laurent@2111:                 app_frame._Refresh(TITLE, FILEMENU, PROJECTTREE)
Laurent@2111:         dialog.Destroy()
andrej@2355: 
Laurent@2111:     def ExtractHexDecValue(self, value):
Laurent@2111:         return ExtractHexDecValue(value)
Laurent@2111: 
Laurent@2111:     def GetSizeOfType(self, type):
Laurent@2111:         return TYPECONVERSION.get(self.GetCTRoot().GetBaseType(type), None)
Laurent@2111: 
Laurent@2111:     def ConfigFileName(self):
Laurent@2111:         return os.path.join(self.CTNPath(), "config.xml")
andrej@2355: 
Laurent@2111:     def ProcessVariablesFileName(self):
Laurent@2111:         return os.path.join(self.CTNPath(), "process_variables.xml")
andrej@2355: 
Laurent@2111:     def FilterSlave(self, slave, vendor=None, slave_pos=None, slave_profile=None):
Laurent@2111:         if slave_pos is not None and slave.getInfo().getPhysAddr() != slave_pos:
Laurent@2111:             return False
Laurent@2111:         type_infos = slave.getType()
Laurent@2111:         if vendor is not None and ExtractHexDecValue(type_infos["vendor"]) != vendor:
Laurent@2111:             return False
andrej@2406:         device, _module_extra_params = self.GetModuleInfos(type_infos)
Laurent@2111:         if slave_profile is not None and slave_profile not in device.GetProfileNumbers():
Laurent@2111:             return False
Laurent@2111:         return True
Laurent@2111: 
Laurent@2124:     def GetSlaveName(self, slave_pos):
Laurent@2124:         CTNChild = self.GetChildByIECLocation((slave_pos,))
Laurent@2124:         if CTNChild is not None:
Laurent@2124:             return CTNChild.CTNName()
Laurent@2124:         return self.CTNName()
Laurent@2124: 
Laurent@2111:     def GetSlaves(self, vendor=None, slave_pos=None, slave_profile=None):
Laurent@2111:         slaves = []
Laurent@2111:         for slave in self.Config.getConfig().getSlave():
Laurent@2111:             if self.FilterSlave(slave, vendor, slave_pos, slave_profile):
Laurent@2111:                 slaves.append(slave.getInfo().getPhysAddr())
Laurent@2111:         slaves.sort()
Laurent@2111:         return slaves
Laurent@2111: 
Laurent@2111:     def GetSlave(self, slave_pos):
Laurent@2111:         for slave in self.Config.getConfig().getSlave():
Laurent@2111:             slave_info = slave.getInfo()
Laurent@2111:             if slave_info.getPhysAddr() == slave_pos:
Laurent@2111:                 return slave
Laurent@2111:         return None
Laurent@2111: 
Laurent@2111:     def GetStartupCommands(self, vendor=None, slave_pos=None, slave_profile=None):
Laurent@2111:         commands = []
Laurent@2111:         for slave in self.Config.getConfig().getSlave():
Laurent@2111:             if self.FilterSlave(slave, vendor, slave_pos, slave_profile):
Laurent@2111:                 commands.append((slave.getInfo().getPhysAddr(), slave.getStartupCommands()))
Laurent@2111:         commands.sort()
Laurent@2111:         return reduce(lambda x, y: x + y[1], commands, [])
andrej@2355: 
Laurent@2111:     def AppendStartupCommand(self, command_infos):
Laurent@2111:         slave = self.GetSlave(command_infos["Position"])
Laurent@2111:         if slave is not None:
Laurent@2111:             command_idx = slave.appendStartupCommand(command_infos)
Laurent@2111:             self.BufferModel()
Laurent@2111:             return command_idx
Laurent@2111:         return None
andrej@2355: 
Laurent@2111:     def SetStartupCommandInfos(self, command_infos):
Laurent@2111:         slave = self.GetSlave(command_infos["Position"])
Laurent@2111:         if slave is not None:
Laurent@2111:             slave.setStartupCommand(command_infos)
Laurent@2111:             self.BufferModel()
andrej@2355: 
Laurent@2111:     def RemoveStartupCommand(self, slave_pos, command_idx, buffer=True):
Laurent@2111:         slave = self.GetSlave(slave_pos)
Laurent@2111:         if slave is not None:
Laurent@2111:             slave.removeStartupCommand(command_idx)
Laurent@2111:             if buffer:
Laurent@2111:                 self.BufferModel()
andrej@2355: 
Laurent@2111:     def SetProcessVariables(self, variables):
Laurent@2111:         vars = []
Laurent@2111:         for var in variables:
Laurent@2157:             variable = ProcessVariablesParser.CreateElement("variable", "ProcessVariables")
Laurent@2111:             variable.setName(var["Name"])
Laurent@2111:             variable.setComment(var["Description"])
Laurent@2111:             if var["ReadFrom"] != "":
Laurent@2111:                 position, index, subindex = var["ReadFrom"]
Laurent@2111:                 if variable.getReadFrom() is None:
Laurent@2111:                     variable.addReadFrom()
Laurent@2111:                 read_from = variable.getReadFrom()
Laurent@2111:                 read_from.setPosition(position)
Laurent@2111:                 read_from.setIndex(index)
Laurent@2111:                 read_from.setSubIndex(subindex)
Laurent@2111:             elif variable.getReadFrom() is not None:
Laurent@2111:                 variable.deleteReadFrom()
Laurent@2111:             if var["WriteTo"] != "":
Laurent@2111:                 position, index, subindex = var["WriteTo"]
Laurent@2111:                 if variable.getWriteTo() is None:
Laurent@2111:                     variable.addWriteTo()
Laurent@2111:                 write_to = variable.getWriteTo()
Laurent@2111:                 write_to.setPosition(position)
Laurent@2111:                 write_to.setIndex(index)
Laurent@2111:                 write_to.setSubIndex(subindex)
Laurent@2111:             elif variable.getWriteTo() is not None:
Laurent@2111:                 variable.deleteWriteTo()
Laurent@2111:             vars.append(variable)
Laurent@2111:         self.ProcessVariables.setvariable(vars)
Laurent@2111:         self.BufferModel()
andrej@2355: 
Laurent@2111:     def GetProcessVariables(self):
Laurent@2111:         variables = []
Laurent@2111:         idx = 0
Laurent@2111:         for variable in self.ProcessVariables.getvariable():
Laurent@2111:             var = {"Name": variable.getName(),
Laurent@2111:                    "Number": idx,
Laurent@2111:                    "Description": variable.getComment()}
Laurent@2111:             read_from = variable.getReadFrom()
Laurent@2111:             if read_from is not None:
Laurent@2111:                 var["ReadFrom"] = (read_from.getPosition(),
Laurent@2111:                                    read_from.getIndex(),
Laurent@2111:                                    read_from.getSubIndex())
Laurent@2111:             else:
Laurent@2111:                 var["ReadFrom"] = ""
Laurent@2111:             write_to = variable.getWriteTo()
Laurent@2111:             if write_to is not None:
Laurent@2111:                 var["WriteTo"] = (write_to.getPosition(),
andrej@2380:                                   write_to.getIndex(),
andrej@2380:                                   write_to.getSubIndex())
Laurent@2111:             else:
Laurent@2111:                 var["WriteTo"] = ""
Laurent@2111:             variables.append(var)
Laurent@2111:             idx += 1
Laurent@2111:         return variables
andrej@2355: 
Laurent@2111:     def _ScanNetwork(self):
Laurent@2111:         app_frame = self.GetCTRoot().AppFrame
andrej@2355: 
Laurent@2111:         execute = True
Laurent@2111:         if len(self.Children) > 0:
andrej@2381:             dialog = wx.MessageDialog(
andrej@2381:                 app_frame,
andrej@2355:                 _("The current network configuration will be deleted.\nDo you want to continue?"),
andrej@2355:                 _("Scan Network"),
andrej@2367:                 wx.YES_NO | wx.ICON_QUESTION)
Laurent@2111:             execute = dialog.ShowModal() == wx.ID_YES
Laurent@2111:             dialog.Destroy()
andrej@2355: 
Laurent@2111:         if execute:
andrej@2366:             error, returnVal = self.RemoteExec(SCAN_COMMAND, returnVal=None)
Laurent@2111:             if error != 0:
andrej@2423:                 dialog = wx.MessageDialog(app_frame, returnVal, _("Error"), wx.OK | wx.ICON_ERROR)
Laurent@2111:                 dialog.ShowModal()
Laurent@2111:                 dialog.Destroy()
Laurent@2111:             elif returnVal is not None:
Laurent@2111:                 for child in self.IECSortedChildren():
Laurent@2111:                     self._doRemoveChild(child)
andrej@2355: 
Laurent@2111:                 for slave in returnVal:
Laurent@2111:                     type_infos = {
Laurent@2111:                         "vendor": slave["vendor_id"],
Laurent@2111:                         "product_code": slave["product_code"],
andrej@2363:                         "revision_number": slave["revision_number"],
Laurent@2111:                     }
andrej@2406:                     device, _module_extra_params = self.GetModuleInfos(type_infos)
Laurent@2111:                     if device is not None:
Laurent@2111:                         if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers():
Laurent@2111:                             CTNType = "EthercatCIA402Slave"
Laurent@2111:                         else:
Laurent@2111:                             CTNType = "EthercatSlave"
Laurent@2111:                         self.CTNAddChild("slave%s" % slave["idx"], CTNType, slave["idx"])
Laurent@2111:                         self.SetSlaveAlias(slave["idx"], slave["alias"])
Laurent@2111:                         type_infos["device_type"] = device.getType().getcontent()
Laurent@2111:                         self.SetSlaveType(slave["idx"], type_infos)
andrej@2355: 
Laurent@2127:                 if app_frame:
Laurent@2127:                     app_frame.RefreshProjectTree()
andrej@2355: 
Laurent@2111:     def CTNAddChild(self, CTNName, CTNType, IEC_Channel=0):
Laurent@2111:         """
Laurent@2111:         Create the confnodes that may be added as child to this node self
Laurent@2111:         @param CTNType: string desining the confnode class name (get name from CTNChildrenTypes)
Laurent@2111:         @param CTNName: string for the name of the confnode instance
Laurent@2111:         """
Laurent@2111:         newConfNodeOpj = ConfigTreeNode.CTNAddChild(self, CTNName, CTNType, IEC_Channel)
andrej@2355: 
Laurent@2111:         slave = self.GetSlave(newConfNodeOpj.BaseParams.getIEC_Channel())
Laurent@2111:         if slave is None:
Laurent@2157:             slave = EtherCATConfigParser.CreateElement("Slave", "Config")
Laurent@2157:             self.Config.getConfig().appendSlave(slave)
Laurent@2111:             slave_infos = slave.getInfo()
Laurent@2111:             slave_infos.setName("undefined")
Laurent@2111:             slave_infos.setPhysAddr(newConfNodeOpj.BaseParams.getIEC_Channel())
Laurent@2111:             slave_infos.setAutoIncAddr(0)
Laurent@2111:             self.BufferModel()
Laurent@2111:             self.OnCTNSave()
andrej@2355: 
Laurent@2111:         return newConfNodeOpj
Laurent@2111: 
Laurent@2111:     def _doRemoveChild(self, CTNInstance):
Laurent@2111:         slave_pos = CTNInstance.GetSlavePos()
Laurent@2111:         config = self.Config.getConfig()
Laurent@2111:         for idx, slave in enumerate(config.getSlave()):
Laurent@2111:             slave_infos = slave.getInfo()
Laurent@2111:             if slave_infos.getPhysAddr() == slave_pos:
Laurent@2111:                 config.removeSlave(idx)
Laurent@2111:                 self.BufferModel()
Laurent@2111:                 self.OnCTNSave()
Laurent@2111:         ConfigTreeNode._doRemoveChild(self, CTNInstance)
Laurent@2111: 
Laurent@2111:     def SetSlavePosition(self, slave_pos, new_pos):
Laurent@2111:         slave = self.GetSlave(slave_pos)
Laurent@2111:         if slave is not None:
Laurent@2111:             slave_info = slave.getInfo()
Laurent@2111:             slave_info.setPhysAddr(new_pos)
Laurent@2111:             for variable in self.ProcessVariables.getvariable():
Laurent@2111:                 read_from = variable.getReadFrom()
Laurent@2111:                 if read_from is not None and read_from.getPosition() == slave_pos:
Laurent@2111:                     read_from.setPosition(new_pos)
Laurent@2111:                 write_to = variable.getWriteTo()
Laurent@2111:                 if write_to is not None and write_to.getPosition() == slave_pos:
Laurent@2111:                     write_to.setPosition(new_pos)
Laurent@2111:             self.CreateBuffer(True)
Laurent@2147:             self.CTNRequestSave()
Laurent@2111:             if self._View is not None:
Laurent@2111:                 self._View.RefreshView()
Laurent@2111:                 self._View.RefreshBuffer()
andrej@2355: 
Laurent@2111:     def GetSlaveAlias(self, slave_pos):
Laurent@2111:         slave = self.GetSlave(slave_pos)
Laurent@2111:         if slave is not None:
Laurent@2111:             slave_info = slave.getInfo()
Laurent@2111:             return slave_info.getAutoIncAddr()
Laurent@2111:         return None
andrej@2355: 
Laurent@2111:     def SetSlaveAlias(self, slave_pos, alias):
Laurent@2111:         slave = self.GetSlave(slave_pos)
Laurent@2111:         if slave is not None:
Laurent@2111:             slave_info = slave.getInfo()
Laurent@2111:             slave_info.setAutoIncAddr(alias)
Laurent@2111:             self.BufferModel()
andrej@2355: 
Laurent@2111:     def GetSlaveType(self, slave_pos):
Laurent@2111:         slave = self.GetSlave(slave_pos)
Laurent@2111:         if slave is not None:
Laurent@2111:             return slave.getType()
Laurent@2111:         return None
andrej@2355: 
Laurent@2111:     def SetSlaveType(self, slave_pos, type_infos):
Laurent@2111:         slave = self.GetSlave(slave_pos)
Laurent@2111:         if slave is not None:
Laurent@2111:             slave.setType(type_infos)
Laurent@2111:             self.BufferModel()
andrej@2355: 
Laurent@2111:     def GetSlaveInfos(self, slave_pos):
Laurent@2111:         slave = self.GetSlave(slave_pos)
Laurent@2111:         if slave is not None:
Laurent@2111:             type_infos = slave.getType()
andrej@2406:             device, _module_extra_params = self.GetModuleInfos(type_infos)
Laurent@2111:             if device is not None:
Laurent@2111:                 infos = type_infos.copy()
Laurent@2111:                 infos.update({"physics": device.getPhysics(),
Laurent@2111:                               "sync_managers": device.GetSyncManagers(),
Laurent@2111:                               "entries": self.GetSlaveVariables(device)})
Laurent@2111:                 return infos
Laurent@2111:         return None
andrej@2355: 
Laurent@2111:     def GetSlaveVariables(self, slave_pos=None, limits=None, device=None):
Laurent@2111:         if device is None and slave_pos is not None:
Laurent@2111:             slave = self.GetSlave(slave_pos)
Laurent@2111:             if slave is not None:
Laurent@2111:                 type_infos = slave.getType()
andrej@2406:                 device, _module_extra_params = self.GetModuleInfos(type_infos)
Laurent@2111:         if device is not None:
Laurent@2111:             entries = device.GetEntriesList(limits)
Laurent@2111:             entries_list = entries.items()
Laurent@2111:             entries_list.sort()
Laurent@2111:             entries = []
Laurent@2111:             current_index = None
andrej@2403:             current_entry = {}
andrej@2406:             for (index, _subindex), entry in entries_list:
Laurent@2111:                 entry["children"] = []
Laurent@2111:                 if slave_pos is not None:
Laurent@2111:                     entry["Position"] = str(slave_pos)
Laurent@2111:                 if index != current_index:
Laurent@2111:                     current_index = index
Laurent@2111:                     current_entry = entry
Laurent@2111:                     entries.append(entry)
andrej@2403:                 elif current_entry:
Laurent@2111:                     current_entry["children"].append(entry)
Laurent@2111:                 else:
Laurent@2111:                     entries.append(entry)
Laurent@2111:             return entries
Laurent@2111:         return []
andrej@2355: 
Laurent@2111:     def GetSlaveVariableDataType(self, slave_pos, index, subindex):
Laurent@2111:         slave = self.GetSlave(slave_pos)
Laurent@2111:         if slave is not None:
andrej@2406:             device, _module_extra_params = self.GetModuleInfos(slave.getType())
Laurent@2111:             if device is not None:
Laurent@2111:                 entries = device.GetEntriesList()
Laurent@2111:                 entry_infos = entries.get((index, subindex))
Laurent@2111:                 if entry_infos is not None:
Laurent@2111:                     return entry_infos["Type"]
Laurent@2111:         return None
andrej@2355: 
Laurent@2111:     def GetNodesVariables(self, vendor=None, slave_pos=None, slave_profile=None, limits=None):
Laurent@2111:         entries = []
Laurent@2111:         for slave_position in self.GetSlaves():
Laurent@2111:             if slave_pos is not None and slave_position != slave_pos:
Laurent@2111:                 continue
Laurent@2111:             slave = self.GetSlave(slave_position)
Laurent@2111:             type_infos = slave.getType()
Laurent@2111:             if vendor is not None and ExtractHexDecValue(type_infos["vendor"]) != vendor:
Laurent@2111:                 continue
andrej@2406:             device, _module_extra_params = self.GetModuleInfos(type_infos)
Laurent@2111:             if slave_profile is not None and slave_profile not in device.GetProfileNumbers():
Laurent@2111:                 continue
Laurent@2111:             entries.extend(self.GetSlaveVariables(slave_position, limits, device))
Laurent@2111:         return entries
andrej@2355: 
Laurent@2111:     def GetModuleInfos(self, type_infos):
Laurent@2111:         return self.CTNParent.GetModuleInfos(type_infos)
andrej@2355: 
Laurent@2111:     def GetSlaveTypesLibrary(self, profile_filter=None):
Laurent@2111:         return self.CTNParent.GetModulesLibrary(profile_filter)
andrej@2355: 
Laurent@2111:     def GetLibraryVendors(self):
Laurent@2111:         return self.CTNParent.GetVendors()
andrej@2355: 
Laurent@2111:     def GetDeviceLocationTree(self, slave_pos, current_location, device_name):
Laurent@2111:         slave = self.GetSlave(slave_pos)
andrej@2355:         vars = []
Laurent@2111:         if slave is not None:
Laurent@2111:             type_infos = slave.getType()
andrej@2355: 
andrej@2406:             device, _module_extra_params = self.GetModuleInfos(type_infos)
Laurent@2111:             if device is not None:
Laurent@2111:                 sync_managers = []
Laurent@2111:                 for sync_manager in device.getSm():
Laurent@2111:                     sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte())
Laurent@2111:                     sync_manager_direction = sync_manager_control_byte & 0x0c
Laurent@2111:                     if sync_manager_direction:
Laurent@2111:                         sync_managers.append(LOCATION_VAR_OUTPUT)
Laurent@2111:                     else:
Laurent@2111:                         sync_managers.append(LOCATION_VAR_INPUT)
andrej@2355: 
Laurent@2111:                 entries = device.GetEntriesList().items()
Laurent@2111:                 entries.sort()
Laurent@2111:                 for (index, subindex), entry in entries:
Laurent@2111:                     var_size = self.GetSizeOfType(entry["Type"])
Laurent@2111:                     if var_size is not None:
Laurent@2111:                         var_class = VARCLASSCONVERSION.get(entry["PDOMapping"], None)
Laurent@2111:                         if var_class is not None:
Laurent@2111:                             if var_class == LOCATION_VAR_INPUT:
Laurent@2111:                                 var_dir = "%I"
Laurent@2111:                             else:
andrej@2355:                                 var_dir = "%Q"
andrej@2355: 
andrej@2380:                             vars.append({
andrej@2380:                                 "name": "0x%4.4x-0x%2.2x: %s" % (index, subindex, entry["Name"]),
andrej@2380:                                 "type": var_class,
andrej@2380:                                 "size": var_size,
andrej@2380:                                 "IEC_type": entry["Type"],
andrej@2380:                                 "var_name": "%s_%4.4x_%2.2x" % ("_".join(device_name.split()), index, subindex),
andrej@2380:                                 "location": "%s%s%s" % (var_dir, var_size, ".".join(map(str, current_location +
andrej@2380:                                                                                         (index, subindex)))),
andrej@2380:                                 "description": "",
andrej@2380:                                 "children": [],
andrej@2380:                             })
andrej@2355: 
Laurent@2111:         return vars
andrej@2355: 
Laurent@2111:     def CTNTestModified(self):
andrej@2355:         return self.ChangesToSave or not self.ModelIsSaved()
Laurent@2111: 
Laurent@2133:     def OnCTNSave(self, from_project_path=None):
Laurent@2111:         config_filepath = self.ConfigFileName()
andrej@2355: 
andrej@2363:         config_xmlfile = open(config_filepath, "w")
Laurent@2157:         config_xmlfile.write(etree.tostring(
andrej@2355:             self.Config,
andrej@2355:             pretty_print=True,
andrej@2355:             xml_declaration=True,
Laurent@2157:             encoding='utf-8'))
Laurent@2111:         config_xmlfile.close()
andrej@2355: 
Laurent@2111:         process_filepath = self.ProcessVariablesFileName()
andrej@2355: 
andrej@2363:         process_xmlfile = open(process_filepath, "w")
Laurent@2157:         process_xmlfile.write(etree.tostring(
andrej@2355:             self.ProcessVariables,
andrej@2355:             pretty_print=True,
andrej@2355:             xml_declaration=True,
Laurent@2157:             encoding='utf-8'))
Laurent@2111:         process_xmlfile.close()
andrej@2355: 
Laurent@2111:         self.Buffer.CurrentSaved()
Laurent@2111:         return True
Laurent@2111: 
Laurent@2111:     def GetProcessVariableName(self, location, var_type):
Laurent@2111:         return "__M%s_%s" % (self.GetSizeOfType(var_type), "_".join(map(str, location)))
Laurent@2111: 
Laurent@2111:     def _Generate_C(self, buildpath, locations):
Laurent@2111:         current_location = self.GetCurrentLocation()
Laurent@2111:         # define a unique name for the generated C file
andrej@2391:         location_str = "_".join(map(str, current_location))
andrej@2355: 
andrej@2358:         Gen_Ethercatfile_path = os.path.join(buildpath, "ethercat_%s.c" % location_str)
andrej@2355: 
Laurent@2111:         self.FileGenerator = _EthercatCFileGenerator(self)
andrej@2355: 
Laurent@2111:         LocationCFilesAndCFLAGS, LDFLAGS, extra_files = ConfigTreeNode._Generate_C(self, buildpath, locations)
andrej@2355: 
Laurent@2111:         for idx, variable in enumerate(self.ProcessVariables.getvariable()):
Laurent@2111:             name = None
Laurent@2111:             var_type = None
Laurent@2111:             read_from = variable.getReadFrom()
Laurent@2111:             write_to = variable.getWriteTo()
Laurent@2111:             if read_from is not None:
Laurent@2111:                 pos = read_from.getPosition()
Laurent@2111:                 index = read_from.getIndex()
Laurent@2111:                 subindex = read_from.getSubIndex()
Laurent@2111:                 location = current_location + (idx, )
Laurent@2111:                 var_type = self.GetSlaveVariableDataType(pos, index, subindex)
andrej@2407:                 name = self.FileGenerator.DeclareVariable(pos, index, subindex,
andrej@2407:                                                           var_type, "I",
andrej@2407:                                                           self.GetProcessVariableName(location, var_type))
Laurent@2111:             if write_to is not None:
Laurent@2111:                 pos = write_to.getPosition()
Laurent@2111:                 index = write_to.getIndex()
Laurent@2111:                 subindex = write_to.getSubIndex()
Laurent@2111:                 if name is None:
Laurent@2111:                     location = current_location + (idx, )
Laurent@2111:                     var_type = self.GetSlaveVariableDataType(pos, index, subindex)
Laurent@2111:                     name = self.GetProcessVariableName(location, var_type)
andrej@2407:                 self.FileGenerator.DeclareVariable(pos, index, subindex, var_type, "Q", name, True)
andrej@2355: 
Laurent@2111:         self.FileGenerator.GenerateCFile(Gen_Ethercatfile_path, location_str, self.BaseParams.getIEC_Channel())
andrej@2355: 
andrej@2381:         LocationCFilesAndCFLAGS.insert(
andrej@2381:             0,
andrej@2355:             (current_location,
andrej@2358:              [(Gen_Ethercatfile_path, '"-I%s"' % os.path.abspath(self.GetCTRoot().GetIECLibPath()))],
Laurent@2111:              True))
Edouard@2112:         LDFLAGS.append("-lethercat_rtdm -lrtdm")
andrej@2355: 
Laurent@2111:         return LocationCFilesAndCFLAGS, LDFLAGS, extra_files
Laurent@2111: 
Laurent@2111:     ConfNodeMethods = [
andrej@2375:         {
andrej@2375:             "bitmap": "ScanNetwork",
andrej@2375:             "name": _("Scan Network"),
andrej@2375:             "tooltip": _("Scan Network"),
andrej@2375:             "method": "_ScanNetwork",
andrej@2375:         },
Laurent@2111:     ]
Laurent@2111: 
Laurent@2111:     def CTNGenerate_C(self, buildpath, locations):
Laurent@2111:         current_location = self.GetCurrentLocation()
andrej@2355: 
Laurent@2111:         slaves = self.GetSlaves()
Laurent@2111:         for slave_pos in slaves:
Laurent@2111:             slave = self.GetSlave(slave_pos)
Laurent@2111:             if slave is not None:
Laurent@2111:                 self.FileGenerator.DeclareSlave(slave_pos, slave)
andrej@2355: 
Laurent@2111:         for location in locations:
Laurent@2111:             loc = location["LOC"][len(current_location):]
Laurent@2111:             slave_pos = loc[0]
Laurent@2111:             if slave_pos in slaves and len(loc) == 3 and location["DIR"] != "M":
Laurent@2111:                 self.FileGenerator.DeclareVariable(
Laurent@2111:                     slave_pos, loc[1], loc[2], location["IEC_TYPE"], location["DIR"], location["NAME"])
andrej@2355: 
andrej@2363:         return [], "", False
andrej@2355: 
andrej@2356: # -------------------------------------------------------------------------------
Laurent@2111: #                      Current Buffering Management Functions
andrej@2356: # -------------------------------------------------------------------------------
Laurent@2111: 
Laurent@2111:     def Copy(self, model):
andrej@2394:         """Return a copy of the config"""
Laurent@2157:         return deepcopy(model)
andrej@2355: 
Laurent@2111:     def CreateBuffer(self, saved):
Laurent@2157:         self.Buffer = UndoBuffer(
andrej@2355:             (EtherCATConfigParser.Dumps(self.Config),
andrej@2355:              ProcessVariablesParser.Dumps(self.ProcessVariables)),
Laurent@2157:             saved)
andrej@2355: 
Laurent@2111:     def BufferModel(self):
Laurent@2157:         self.Buffer.Buffering(
andrej@2355:             (EtherCATConfigParser.Dumps(self.Config),
Laurent@2157:              ProcessVariablesParser.Dumps(self.ProcessVariables)))
andrej@2355: 
Laurent@2111:     def ModelIsSaved(self):
Laurent@2111:         if self.Buffer is not None:
Laurent@2111:             return self.Buffer.IsCurrentSaved()
Laurent@2111:         else:
Laurent@2111:             return True
Laurent@2111: 
Laurent@2111:     def LoadPrevious(self):
Laurent@2157:         config, process_variables = self.Buffer.Previous()
Laurent@2157:         self.Config = EtherCATConfigParser.Loads(config)
Laurent@2157:         self.ProcessVariables = ProcessVariablesParser.Loads(process_variables)
andrej@2355: 
Laurent@2111:     def LoadNext(self):
Laurent@2157:         config, process_variables = self.Buffer.Next()
Laurent@2157:         self.Config = EtherCATConfigParser.Loads(config)
Laurent@2157:         self.ProcessVariables = ProcessVariablesParser.Loads(process_variables)
andrej@2355: 
Laurent@2111:     def GetBufferState(self):
Laurent@2111:         first = self.Buffer.IsFirst()
Laurent@2111:         last = self.Buffer.IsLast()
Laurent@2111:         return not first, not last