etisserant@163: import os, sys
etisserant@20: base_folder = os.path.split(sys.path[0])[0]
etisserant@49: CanFestivalPath = os.path.join(base_folder, "CanFestival-3")
etisserant@49: sys.path.append(os.path.join(CanFestivalPath, "objdictgen"))
etisserant@20: 
lbessard@11: from nodelist import NodeList
lbessard@11: from nodemanager import NodeManager
etisserant@178: import config_utils, gen_cfile, eds_utils
etisserant@12: from networkedit import networkedit
lbessard@77: from objdictedit import objdictedit
etisserant@49: import canfestival_config
greg@95: from plugger import PlugTemplate
etisserant@169: from commondialogs import CreateNodeDialog
etisserant@169: import wx
lbessard@11: 
lbessard@77: from gnosis.xml.pickle import *
lbessard@77: from gnosis.xml.pickle.util import setParanoia
lbessard@77: setParanoia(0)
lbessard@77: 
etisserant@163: #--------------------------------------------------
etisserant@163: #                    SLAVE
etisserant@163: #--------------------------------------------------
etisserant@163: 
etisserant@163: class _SlavePlug(NodeManager):
etisserant@12:     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
etisserant@12:     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
etisserant@166:       <xsd:element name="CanFestivalSlaveNode">
etisserant@12:         <xsd:complexType>
lbessard@86:           <xsd:attribute name="CAN_Device" type="xsd:string" use="required"/>
lbessard@86:           <xsd:attribute name="CAN_Baudrate" type="xsd:string" use="required"/>
lbessard@86:           <xsd:attribute name="NodeId" type="xsd:string" use="required"/>
etisserant@178:           <xsd:attribute name="Sync_Align" type="xsd:integer" use="optional" default="0"/>
etisserant@203:           <xsd:attribute name="Sync_Align_Ratio" use="optional" default="50">
etisserant@203:             <xsd:simpleType>
etisserant@203:                 <xsd:restriction base="xsd:integer">
etisserant@203:                     <xsd:minInclusive value="1"/>
etisserant@203:                     <xsd:maxInclusive value="99"/>
etisserant@203:                 </xsd:restriction>
etisserant@203:             </xsd:simpleType>
etisserant@203:           </xsd:attribute>
etisserant@12:         </xsd:complexType>
etisserant@12:       </xsd:element>
etisserant@12:     </xsd:schema>
etisserant@12:     """
etisserant@12: 
etisserant@163:     def GetSlaveODPath(self):
etisserant@166:         return os.path.join(self.PlugPath(), 'slave.od')
etisserant@163: 
lbessard@17:     def __init__(self):
etisserant@23:         # TODO change netname when name change
etisserant@163:         NodeManager.__init__(self)
etisserant@163:         odfilepath = self.GetSlaveODPath()
etisserant@163:         if(os.path.isfile(odfilepath)):
etisserant@163:             self.OpenFileInCurrent(odfilepath)
etisserant@163:         else:
etisserant@169:             self.FilePath = ""
etisserant@169:             dialog = CreateNodeDialog(None, wx.OK)
etisserant@169:             dialog.Type.Enable(False)
etisserant@169:             dialog.GenSYNC.Enable(False)
etisserant@169:             if dialog.ShowModal() == wx.ID_OK:
etisserant@169:                 name, id, nodetype, description = dialog.GetValues()
etisserant@169:                 profile, filepath = dialog.GetProfile()
etisserant@169:                 NMT = dialog.GetNMTManagement()
etisserant@169:                 options = dialog.GetOptions()
etisserant@169:                 self.CreateNewNode(name,       # Name - will be changed at build time
etisserant@169:                                    id,         # NodeID - will be changed at build time
etisserant@169:                                    "slave",    # Type
etisserant@169:                                    description,# description 
etisserant@169:                                    profile,    # profile
etisserant@169:                                    filepath,   # prfile filepath
etisserant@169:                                    NMT,        # NMT
etisserant@169:                                    options)     # options
etisserant@169:             else:
etisserant@169:                 self.CreateNewNode("SlaveNode",  # Name - will be changed at build time
etisserant@169:                                    0x00,         # NodeID - will be changed at build time
etisserant@169:                                    "slave",      # Type
etisserant@169:                                    "",           # description 
etisserant@169:                                    "None",       # profile
etisserant@169:                                    "", # prfile filepath
etisserant@169:                                    "heartbeat",  # NMT
etisserant@169:                                    [])           # options
etisserant@169:             dialog.Destroy()
etisserant@13:     _View = None
etisserant@203:     def _OpenView(self):
etisserant@13:         if not self._View:
etisserant@13:             def _onclose():
etisserant@30:                 self._View = None
lbessard@25:             def _onsave():
lbessard@25:                 self.GetPlugRoot().SaveProject()
etisserant@166:             self._View = objdictedit(self.GetPlugRoot().AppFrame, self)
etisserant@23:             # TODO redefine BusId when IEC channel change
etisserant@23:             self._View.SetBusId(self.GetCurrentLocation())
etisserant@13:             self._View._onclose = _onclose
lbessard@25:             self._View._onsave = _onsave
etisserant@23:             self._View.Show()
etisserant@23: 
lbessard@65:     PluginMethods = [
lbessard@82:         {"bitmap" : os.path.join("images", "NetworkEdit"),
etisserant@166:          "name" : "Edit slave", 
etisserant@166:          "tooltip" : "Edit CanOpen slave with ObjdictEdit",
etisserant@105:          "method" : "_OpenView"},
lbessard@65:     ]
etisserant@13: 
etisserant@13:     def OnPlugClose(self):
etisserant@13:         if self._View:
etisserant@13:             self._View.Close()
etisserant@13: 
etisserant@13:     def PlugTestModified(self):
etisserant@166:         return self.ChangesToSave or self.OneFileHasChanged()
etisserant@12:         
lbessard@17:     def OnPlugSave(self):
etisserant@166:         return self.SaveCurrentInFile(self.GetSlaveODPath())
etisserant@12: 
etisserant@203:     def PlugGenerate_C(self, buildpath, locations):
etisserant@12:         """
etisserant@15:         Generate C code
etisserant@15:         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
etisserant@15:         @param locations: List of complete variables locations \
etisserant@22:             [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
etisserant@22:             "NAME" : name of the variable (generally "__IW0_1_2" style)
etisserant@22:             "DIR" : direction "Q","I" or "M"
etisserant@22:             "SIZE" : size "X", "B", "W", "D", "L"
etisserant@22:             "LOC" : tuple of interger for IEC location (0,1,2,...)
etisserant@22:             }, ...]
etisserant@22:         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
etisserant@12:         """
etisserant@24:         current_location = self.GetCurrentLocation()
etisserant@22:         # define a unique name for the generated C file
etisserant@166:         prefix = "_".join(map(str, current_location))
etisserant@49:         Gen_OD_path = os.path.join(buildpath, "OD_%s.c"%prefix )
etisserant@166:         # Create a new copy of the model
etisserant@166:         slave = self.GetCurrentNodeCopy()
etisserant@166:         slave.SetNodeName("OD_%s"%prefix)
etisserant@166:         # allow access to local OD from Slave PLC
etisserant@166:         pointers = config_utils.LocalODPointers(locations, current_location, slave)
etisserant@166:         res = gen_cfile.GenerateFile(Gen_OD_path, slave, pointers)
etisserant@15:         if res :
etisserant@15:             raise Exception, res
lbessard@172:         res = eds_utils.GenerateEDSFile(os.path.join(buildpath, "Slave_%s.eds"%prefix), slave)
lbessard@172:         if res :
lbessard@172:             raise Exception, res
etisserant@52:         return [(Gen_OD_path,canfestival_config.getCFLAGS(CanFestivalPath))],"",False
etisserant@163: 
etisserant@163: #--------------------------------------------------
etisserant@163: #                    MASTER
etisserant@163: #--------------------------------------------------
etisserant@163: 
etisserant@163: class _NodeListPlug(NodeList):
etisserant@163:     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
etisserant@163:     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
etisserant@163:       <xsd:element name="CanFestivalNode">
etisserant@163:         <xsd:complexType>
etisserant@163:           <xsd:attribute name="CAN_Device" type="xsd:string" use="required"/>
etisserant@163:           <xsd:attribute name="CAN_Baudrate" type="xsd:string" use="required"/>
etisserant@163:           <xsd:attribute name="NodeId" type="xsd:string" use="required"/>
etisserant@163:           <xsd:attribute name="Sync_TPDOs" type="xsd:boolean" use="optional" default="true"/>
etisserant@163:         </xsd:complexType>
etisserant@163:       </xsd:element>
etisserant@163:     </xsd:schema>
etisserant@163:     """
etisserant@163: 
etisserant@163:     def __init__(self):
etisserant@163:         manager = NodeManager()
etisserant@163:         # TODO change netname when name change
etisserant@163:         NodeList.__init__(self, manager, self.BaseParams.getName())
etisserant@163:         self.LoadProject(self.PlugPath())
etisserant@163: 
etisserant@163:     _View = None
etisserant@203:     def _OpenView(self):
etisserant@163:         if not self._View:
etisserant@163:             def _onclose():
etisserant@163:                 self._View = None
etisserant@163:             def _onsave():
etisserant@163:                 self.GetPlugRoot().SaveProject()
etisserant@166:             self._View = networkedit(self.GetPlugRoot().AppFrame, self)
etisserant@163:             # TODO redefine BusId when IEC channel change
etisserant@163:             self._View.SetBusId(self.GetCurrentLocation())
etisserant@163:             self._View._onclose = _onclose
etisserant@163:             self._View._onsave = _onsave
etisserant@163:             self._View.Show()
etisserant@163: 
etisserant@203:     def _ShowMasterGenerated(self):
etisserant@163:         buildpath = self._getBuildPath()
etisserant@163:         # Eventually create build dir
etisserant@163:         if not os.path.exists(buildpath):
etisserant@203:             self.logger.write_error("Error: No PLC built\n")
etisserant@163:             return
etisserant@163:         
etisserant@163:         masterpath = os.path.join(buildpath, "MasterGenerated.od")
etisserant@163:         if not os.path.exists(masterpath):
etisserant@203:             self.logger.write_error("Error: No Master generated\n")
etisserant@163:             return
etisserant@163:         
etisserant@169:         new_dialog = objdictedit(None, filesOpen=[masterpath])
etisserant@163:         new_dialog.Show()
etisserant@163: 
etisserant@163:     PluginMethods = [
etisserant@163:         {"bitmap" : os.path.join("images", "NetworkEdit"),
etisserant@163:          "name" : "Edit network", 
etisserant@163:          "tooltip" : "Edit CanOpen Network with NetworkEdit",
etisserant@163:          "method" : "_OpenView"},
etisserant@163:         {"name" : "Show Master", 
etisserant@163:          "tooltip" : "Show Master generated by config_utils",
etisserant@163:          "method" : "_ShowMasterGenerated"}
etisserant@163:     ]
etisserant@163: 
etisserant@163:     def OnPlugClose(self):
etisserant@163:         if self._View:
etisserant@163:             self._View.Close()
etisserant@163: 
etisserant@163:     def PlugTestModified(self):
etisserant@163:         return self.ChangesToSave or self.HasChanged()
etisserant@163:         
etisserant@163:     def OnPlugSave(self):
etisserant@163:         self.SetRoot(self.PlugPath())
lbessard@250:         return self.SaveProject() is None
etisserant@163: 
etisserant@203:     def PlugGenerate_C(self, buildpath, locations):
etisserant@163:         """
etisserant@163:         Generate C code
etisserant@163:         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
etisserant@163:         @param locations: List of complete variables locations \
etisserant@163:             [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
etisserant@163:             "NAME" : name of the variable (generally "__IW0_1_2" style)
etisserant@163:             "DIR" : direction "Q","I" or "M"
etisserant@163:             "SIZE" : size "X", "B", "W", "D", "L"
etisserant@163:             "LOC" : tuple of interger for IEC location (0,1,2,...)
etisserant@163:             }, ...]
etisserant@163:         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
etisserant@163:         """
etisserant@163:         current_location = self.GetCurrentLocation()
etisserant@163:         # define a unique name for the generated C file
etisserant@166:         prefix = "_".join(map(str, current_location))
etisserant@163:         Gen_OD_path = os.path.join(buildpath, "OD_%s.c"%prefix )
etisserant@163:         # Create a new copy of the model with DCF loaded with PDO mappings for desired location
greg@341:         try:
greg@341:             master, pointers = config_utils.GenerateConciseDCF(locations, current_location, self, self.CanFestivalNode.getSync_TPDOs(),"OD_%s"%prefix)
greg@341:         except config_utils.PDOmappingException, e:
greg@341:             raise Exception, e.message
etisserant@166:         # Do generate C file.
etisserant@163:         res = gen_cfile.GenerateFile(Gen_OD_path, master, pointers)
etisserant@163:         if res :
etisserant@163:             raise Exception, res
etisserant@163:         
etisserant@163:         file = open(os.path.join(buildpath, "MasterGenerated.od"), "w")
etisserant@163:         dump(master, file)
etisserant@163:         file.close()
etisserant@163:         
etisserant@163:         return [(Gen_OD_path,canfestival_config.getCFLAGS(CanFestivalPath))],"",False
etisserant@12:     
etisserant@13: class RootClass:
etisserant@12:     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
etisserant@12:     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
etisserant@12:       <xsd:element name="CanFestivalInstance">
etisserant@12:         <xsd:complexType>
lbessard@86:           <xsd:attribute name="CAN_Driver" type="xsd:string" use="required"/>
greg@157:           <xsd:attribute name="Debug_mode" type="xsd:boolean" use="optional" default="false"/>
etisserant@12:         </xsd:complexType>
etisserant@12:       </xsd:element>
etisserant@12:     </xsd:schema>
etisserant@12:     """
etisserant@169:     PlugChildsTypes = [("CanOpenNode",_NodeListPlug, "CanOpen Master"),
etisserant@169:                        ("CanOpenSlave",_SlavePlug, "CanOpen Slave")]
greg@95:     def GetParamsAttributes(self, path = None):
greg@95:         infos = PlugTemplate.GetParamsAttributes(self, path = None)
greg@95:         for element in infos:
etisserant@163:             if element["name"] == "CanFestivalInstance":
greg@95:                 for child in element["children"]:
greg@95:                     if child["name"] == "CAN_Driver":
greg@95:                         DLL_LIST= getattr(canfestival_config,"DLL_LIST",None)
greg@95:                         if DLL_LIST is not None:
greg@95:                             child["type"] = DLL_LIST
greg@95:                         return infos    
greg@95:         return infos
lbessard@11: 
etisserant@203:     def PlugGenerate_C(self, buildpath, locations):
etisserant@52:         
etisserant@52:         format_dict = {"locstr" : "_".join(map(str,self.GetCurrentLocation())),
etisserant@52:                        "candriver" : self.CanFestivalInstance.getCAN_Driver(),
etisserant@52:                        "nodes_includes" : "",
etisserant@52:                        "board_decls" : "",
etisserant@52:                        "nodes_init" : "",
etisserant@52:                        "nodes_open" : "",
greg@336:                        "nodes_stop" : "",
etisserant@57:                        "nodes_close" : "",
etisserant@57:                        "nodes_send_sync" : "",
etisserant@169:                        "nodes_proceed_sync" : "",
etisserant@174:                        "slavebootups" : "",
etisserant@178:                        "slavebootup_register" : "",
etisserant@178:                        "post_sync" : "",
etisserant@178:                        "post_sync_register" : "",
etisserant@178:                        }
etisserant@52:         for child in self.IECSortedChilds():
etisserant@52:             childlocstr = "_".join(map(str,child.GetCurrentLocation()))
etisserant@52:             nodename = "OD_%s" % childlocstr
etisserant@166:             
etisserant@166:             # Try to get Slave Node
etisserant@166:             child_data = getattr(child, "CanFestivalSlaveNode", None)
etisserant@166:             if child_data is None:
etisserant@166:                 # Not a slave -> master
etisserant@166:                 child_data = getattr(child, "CanFestivalNode")
etisserant@169:                 # Apply sync setting
etisserant@178:                 format_dict["nodes_init"] += 'NODE_MASTER_INIT(%s, %s)\n    '%(
etisserant@178:                        nodename,
etisserant@178:                        child_data.getNodeId())
etisserant@166:                 if child_data.getSync_TPDOs():
etisserant@166:                     format_dict["nodes_send_sync"] += 'NODE_SEND_SYNC(%s)\n    '%(nodename)
etisserant@166:                     format_dict["nodes_proceed_sync"] += 'NODE_PROCEED_SYNC(%s)\n    '%(nodename)
etisserant@178: 
etisserant@178:                 # initialize and declare node boot status variables for post_SlaveBootup lookup
etisserant@169:                 SlaveIDs = child.GetSlaveIDs()
etisserant@169:                 for id in SlaveIDs:
etisserant@178:                     format_dict["slavebootups"] += (
etisserant@178:                     "int %s_slave_%d_booted = 0;\n"%(nodename, id))
etisserant@178:                 # define post_SlaveBootup lookup functions
etisserant@178:                 format_dict["slavebootups"] += (
etisserant@178:                     "static void %s_post_SlaveBootup(CO_Data* d, UNS8 nodeId){\n"%(nodename)+
etisserant@178:                     "    switch(nodeId){\n")
etisserant@178:                 # one case per declared node, mark node as booted
etisserant@169:                 for id in SlaveIDs:
etisserant@178:                     format_dict["slavebootups"] += (
etisserant@178:                     "        case %d:\n"%(id)+
etisserant@178:                     "            %s_slave_%d_booted = 1;\n"%(nodename, id)+
etisserant@178:                     "            break;\n")
etisserant@178:                 format_dict["slavebootups"] += (
etisserant@178:                     "        default:\n"+
etisserant@178:                     "            break;\n"+
etisserant@178:                     "    }\n"+
etisserant@178:                     "    if( ")
etisserant@178:                 # expression to test if all declared nodes booted
etisserant@169:                 format_dict["slavebootups"] += " && ".join(["%s_slave_%d_booted"%(nodename, id) for id in SlaveIDs])
etisserant@178:                 format_dict["slavebootups"] += " )\n" + (
etisserant@178:                     "        Master_post_SlaveBootup(d,nodeId);\n"+
etisserant@178:                     "}\n")
etisserant@178:                 # register previously declared func as post_SlaveBootup callback for that node
etisserant@178:                 format_dict["slavebootup_register"] += (
etisserant@178:                     "%s_Data.post_SlaveBootup = %s_post_SlaveBootup;\n"%(nodename,nodename))
etisserant@178:             else:
etisserant@178:                 # Slave node
etisserant@178:                 align = child_data.getSync_Align()
etisserant@203:                 align_ratio=child_data.getSync_Align_Ratio()
etisserant@178:                 if align > 0:
etisserant@178:                     format_dict["post_sync"] += (
etisserant@178:                         "static int %s_CalCount = 0;\n"%(nodename)+
etisserant@178:                         "static void %s_post_sync(CO_Data* d){\n"%(nodename)+
etisserant@178:                         "    if(%s_CalCount < %d){\n"%(nodename, align)+
etisserant@178:                         "        %s_CalCount++;\n"%(nodename)+
etisserant@203:                         "        align_tick(-1);\n"+
etisserant@178:                         "    }else{\n"+
etisserant@203:                         "        align_tick(%d);\n"%(align_ratio)+
etisserant@178:                         "    }\n"+
etisserant@178:                         "}\n")
etisserant@178:                     format_dict["post_sync_register"] += (
etisserant@178:                         "%s_Data.post_sync = %s_post_sync;\n"%(nodename,nodename))
etisserant@178:                 format_dict["nodes_init"] += 'NODE_SLAVE_INIT(%s, %s)\n    '%(
etisserant@178:                        nodename,
etisserant@178:                        child_data.getNodeId())
etisserant@178:     
etisserant@178:             # Include generated OD headers
etisserant@52:             format_dict["nodes_includes"] += '#include "%s.h"\n'%(nodename)
etisserant@178:             # Declare CAN channels according user filled config
etisserant@52:             format_dict["board_decls"] += 'BOARD_DECL(%s, "%s", "%s")\n'%(
etisserant@52:                    nodename,
etisserant@166:                    child_data.getCAN_Device(),
etisserant@166:                    child_data.getCAN_Baudrate())
etisserant@57:             format_dict["nodes_open"] += 'NODE_OPEN(%s)\n    '%(nodename)
etisserant@57:             format_dict["nodes_close"] += 'NODE_CLOSE(%s)\n    '%(nodename)
greg@336:             format_dict["nodes_stop"] += 'NODE_STOP(%s)\n    '%(nodename)        
etisserant@163:         if sys.platform == 'win32':
greg@157:             if self.CanFestivalInstance.getDebug_mode() and os.path.isfile(os.path.join("%s"%(format_dict["candriver"] + '_DEBUG.dll'))):
greg@157:                     format_dict["candriver"] += '_DEBUG.dll'
greg@157:             else:
greg@157:                 format_dict["candriver"] += '.dll'
greg@157:         
etisserant@52:         filename = os.path.join(os.path.split(__file__)[0],"cf_runtime.c")
etisserant@52:         cf_main = open(filename).read() % format_dict
etisserant@52:         cf_main_path = os.path.join(buildpath, "CF_%(locstr)s.c"%format_dict)
etisserant@52:         f = open(cf_main_path,'w')
etisserant@52:         f.write(cf_main)
etisserant@52:         f.close()
etisserant@52:         
etisserant@52:         return [(cf_main_path, canfestival_config.getCFLAGS(CanFestivalPath))],canfestival_config.getLDFLAGS(CanFestivalPath), True
lbessard@11: 
etisserant@15: