|
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 |