|
1 import os, shutil |
|
2 import cPickle |
|
3 from xml.dom import minidom |
|
4 |
|
5 import wx |
|
6 |
|
7 from xmlclass import * |
|
8 from PLCControler import UndoBuffer |
|
9 from ConfigEditor import ConfigEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE |
|
10 |
|
11 TYPECONVERSION = {"BOOL" : "X", "SINT" : "B", "INT" : "W", "DINT" : "D", "LINT" : "L", |
|
12 "USINT" : "B", "UINT" : "W", "UDINT" : "D", "ULINT" : "L", |
|
13 "BYTE" : "B", "WORD" : "W", "DWORD" : "D", "LWORD" : "L"} |
|
14 |
|
15 DATATYPECONVERSION = {"BOOL" : "BIT", "SINT" : "S8", "INT" : "S16", "DINT" : "S32", "LINT" : "S64", |
|
16 "USINT" : "U8", "UINT" : "U16", "UDINT" : "U32", "ULINT" : "U64", |
|
17 "BYTE" : "U8", "WORD" : "U16", "DWORD" : "U32", "LWORD" : "U64"} |
|
18 |
|
19 #-------------------------------------------------- |
|
20 # Ethercat MASTER |
|
21 #-------------------------------------------------- |
|
22 |
|
23 EtherCATConfigClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATConfig.xsd")) |
|
24 |
|
25 def ExtractHexDecValue(value): |
|
26 try: |
|
27 return int(value) |
|
28 except: |
|
29 pass |
|
30 try: |
|
31 return int(value.replace("#", "0"), 16) |
|
32 except: |
|
33 raise "Invalid value for HexDecValue \"%s\"" % value |
|
34 |
|
35 def GenerateHexDecValue(value, base=10): |
|
36 if base == 10: |
|
37 return str(value) |
|
38 elif base == 16: |
|
39 return "#x%.8x" % value |
|
40 else: |
|
41 raise "Not supported base" |
|
42 |
|
43 cls = EtherCATConfigClasses.get("Config_Slave", None) |
|
44 if cls: |
|
45 |
|
46 def getType(self): |
|
47 slave_info = self.getInfo() |
|
48 return {"device_type": slave_info.getName(), |
|
49 "vendor": GenerateHexDecValue(slave_info.getVendorId()), |
|
50 "product_code": GenerateHexDecValue(slave_info.getProductCode(), 16), |
|
51 "revision_number": GenerateHexDecValue(slave_info.getRevisionNo(), 16)} |
|
52 setattr(cls, "getType", getType) |
|
53 |
|
54 def setType(self, type_infos): |
|
55 slave_info = self.getInfo() |
|
56 slave_info.setName(type_infos["device_type"]) |
|
57 slave_info.setVendorId(ExtractHexDecValue(type_infos["vendor"])) |
|
58 slave_info.setProductCode(ExtractHexDecValue(type_infos["product_code"])) |
|
59 slave_info.setRevisionNo(ExtractHexDecValue(type_infos["revision_number"])) |
|
60 setattr(cls, "setType", setType) |
|
61 |
|
62 cls = EtherCATConfigClasses.get("Slave_Info", None) |
|
63 if cls: |
|
64 |
|
65 def getSlavePosition(self): |
|
66 return self.getPhysAddr(), self.getAutoIncAddr() |
|
67 setattr(cls, "getSlavePosition", getSlavePosition) |
|
68 |
|
69 def setSlavePosition(self, alias, pos): |
|
70 self.setPhysAddr(alias) |
|
71 self.setAutoIncAddr(pos) |
|
72 setattr(cls, "setSlavePosition", setSlavePosition) |
|
73 |
|
74 class _EthercatPlug: |
|
75 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
|
76 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
|
77 <xsd:element name="EtherlabNode"> |
|
78 <xsd:complexType> |
|
79 <xsd:attribute name="MasterNumber" type="xsd:integer" use="optional" default="0"/> |
|
80 <xsd:attribute name="ConfigurePDOs" type="xsd:boolean" use="optional" default="true"/> |
|
81 </xsd:complexType> |
|
82 </xsd:element> |
|
83 </xsd:schema> |
|
84 """ |
|
85 |
|
86 def __init__(self): |
|
87 filepath = self.ConfigFileName() |
|
88 |
|
89 self.Config = EtherCATConfigClasses["EtherCATConfig"]() |
|
90 if os.path.isfile(filepath): |
|
91 xmlfile = open(filepath, 'r') |
|
92 tree = minidom.parse(xmlfile) |
|
93 xmlfile.close() |
|
94 |
|
95 for child in tree.childNodes: |
|
96 if child.nodeType == tree.ELEMENT_NODE and child.nodeName == "EtherCATConfig": |
|
97 self.Config.loadXMLTree(child, ["xmlns:xsi", "xsi:noNamespaceSchemaLocation"]) |
|
98 self.CreateConfigBuffer(True) |
|
99 else: |
|
100 self.CreateConfigBuffer(False) |
|
101 self.OnPlugSave() |
|
102 |
|
103 def ExtractHexDecValue(self, value): |
|
104 return ExtractHexDecValue(value) |
|
105 |
|
106 def GetSizeOfType(self, type): |
|
107 return TYPECONVERSION.get(self.GetPlugRoot().GetBaseType(type), None) |
|
108 |
|
109 def ConfigFileName(self): |
|
110 return os.path.join(self.PlugPath(), "config.xml") |
|
111 |
|
112 def GetFilename(self): |
|
113 return self.MandatoryParams[1].getName() |
|
114 |
|
115 def GetSlaves(self): |
|
116 slaves = [] |
|
117 for slave in self.Config.getConfig().getSlave(): |
|
118 slaves.append(slave.getInfo().getSlavePosition()) |
|
119 slaves.sort() |
|
120 return slaves |
|
121 |
|
122 def GetSlave(self, slave_pos): |
|
123 for slave in self.Config.getConfig().getSlave(): |
|
124 slave_info = slave.getInfo() |
|
125 if slave_info.getSlavePosition() == slave_pos: |
|
126 return slave |
|
127 return None |
|
128 |
|
129 def AddSlave(self): |
|
130 slaves = self.GetSlaves() |
|
131 if len(slaves) > 0: |
|
132 new_pos = (slaves[-1][0] + 1, 0) |
|
133 else: |
|
134 new_pos = (0, 0) |
|
135 slave = EtherCATConfigClasses["Config_Slave"]() |
|
136 slave_infos = slave.getInfo() |
|
137 slave_infos.setName("undefined") |
|
138 slave_infos.setSlavePosition(new_pos[0], new_pos[1]) |
|
139 self.Config.getConfig().appendSlave(slave) |
|
140 self.BufferConfig() |
|
141 return new_pos |
|
142 |
|
143 def RemoveSlave(self, slave_pos): |
|
144 config = self.Config.getConfig() |
|
145 for idx, slave in enumerate(config.getSlave()): |
|
146 slave_infos = slave.getInfo() |
|
147 if slave_infos.getSlavePosition() == slave_pos: |
|
148 config.removeSlave(idx) |
|
149 self.BufferConfig() |
|
150 return True |
|
151 return False |
|
152 |
|
153 def SetSlavePos(self, slave_pos, alias=None, position=None): |
|
154 slave = self.GetSlave(slave_pos) |
|
155 if slave is not None: |
|
156 slave_info = slave.getInfo() |
|
157 new_pos = slave_pos |
|
158 if alias is not None: |
|
159 new_pos = (alias, new_pos[1]) |
|
160 if position is not None: |
|
161 new_pos = (new_pos[0], position) |
|
162 if self.GetSlave(new_pos) is not None: |
|
163 return _("Slave with position \"%d:%d\" already exists!" % new_pos) |
|
164 slave_info.setSlavePosition(*new_pos) |
|
165 self.BufferConfig() |
|
166 return None |
|
167 |
|
168 def GetSlaveType(self, slave_pos): |
|
169 slave = self.GetSlave(slave_pos) |
|
170 if slave is not None: |
|
171 return slave.getType() |
|
172 return None |
|
173 |
|
174 def SetSlaveType(self, slave_pos, type_infos): |
|
175 slave = self.GetSlave(slave_pos) |
|
176 if slave is not None: |
|
177 slave.setType(type_infos) |
|
178 self.BufferConfig() |
|
179 return None |
|
180 |
|
181 def GetSlaveInfos(self, slave_pos): |
|
182 slave = self.GetSlave(slave_pos) |
|
183 if slave is not None: |
|
184 type_infos = slave.getType() |
|
185 device = self.GetModuleInfos(type_infos) |
|
186 if device is not None: |
|
187 infos = type_infos.copy() |
|
188 infos.update({"physics": device.getPhysics(), |
|
189 "pdos": [], |
|
190 "variables": []}) |
|
191 for TxPdo in device.getTxPdo(): |
|
192 ExtractPdoInfos(TxPdo, "Transmit", infos) |
|
193 for RxPdo in device.getRxPdo(): |
|
194 ExtractPdoInfos(RxPdo, "Receive", infos) |
|
195 return infos |
|
196 return None |
|
197 |
|
198 def GetModuleInfos(self, type_infos): |
|
199 return self.PlugParent.GetModuleInfos(type_infos) |
|
200 |
|
201 def GetSlaveTypesLibrary(self): |
|
202 return self.PlugParent.GetModulesLibrary() |
|
203 |
|
204 def _OpenView(self): |
|
205 app_frame = self.GetPlugRoot().AppFrame |
|
206 |
|
207 configeditor = ConfigEditor(app_frame.TabsOpened, self, app_frame) |
|
208 |
|
209 app_frame.EditProjectElement(configeditor, self.GetFilename()) |
|
210 |
|
211 PluginMethods = [ |
|
212 {"bitmap" : os.path.join("images", "EditCfile"), |
|
213 "name" : _("Edit Config"), |
|
214 "tooltip" : _("Edit Config"), |
|
215 "method" : "_OpenView"}, |
|
216 ] |
|
217 |
|
218 def PlugTestModified(self): |
|
219 return self.ChangesToSave or not self.ConfigIsSaved() |
|
220 |
|
221 def OnPlugSave(self): |
|
222 filepath = self.ConfigFileName() |
|
223 |
|
224 text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" |
|
225 extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", |
|
226 "xsi:noNamespaceSchemaLocation" : "EtherCATInfo.xsd"} |
|
227 text += self.Config.generateXMLText("EtherCATConfig", 0, extras) |
|
228 |
|
229 xmlfile = open(filepath,"w") |
|
230 xmlfile.write(text.encode("utf-8")) |
|
231 xmlfile.close() |
|
232 |
|
233 self.ConfigBuffer.CurrentSaved() |
|
234 return True |
|
235 |
|
236 def PlugGenerate_C(self, buildpath, locations): |
|
237 """ |
|
238 Generate C code |
|
239 @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5) |
|
240 @param locations: List of complete variables locations \ |
|
241 [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...) |
|
242 "NAME" : name of the variable (generally "__IW0_1_2" style) |
|
243 "DIR" : direction "Q","I" or "M" |
|
244 "SIZE" : size "X", "B", "W", "D", "L" |
|
245 "LOC" : tuple of interger for IEC location (0,1,2,...) |
|
246 }, ...] |
|
247 @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND |
|
248 """ |
|
249 current_location = self.GetCurrentLocation() |
|
250 # define a unique name for the generated C file |
|
251 location_str = "_".join(map(lambda x:str(x), current_location)) |
|
252 |
|
253 Gen_Ethercatfile_path = os.path.join(buildpath, "ethercat_%s.c"%location_str) |
|
254 |
|
255 file_generator = _EthercatCFileGenerator(self, Gen_Ethercatfile_path) |
|
256 |
|
257 for location in locations: |
|
258 loc = location["LOC"][len(current_location):] |
|
259 file_generator.DeclareVariable(loc[:2], loc[2], loc[3], location["IEC_TYPE"], location["DIR"], location["NAME"]) |
|
260 |
|
261 file_generator.GenerateCFile() |
|
262 |
|
263 return [(Gen_Ethercatfile_path, '"-I%s"'%os.path.abspath(self.GetPlugRoot().GetIECLibPath()))], "-lethercat -lrtdm", True |
|
264 |
|
265 #------------------------------------------------------------------------------- |
|
266 # Current Buffering Management Functions |
|
267 #------------------------------------------------------------------------------- |
|
268 |
|
269 """ |
|
270 Return a copy of the config |
|
271 """ |
|
272 def Copy(self, model): |
|
273 return cPickle.loads(cPickle.dumps(model)) |
|
274 |
|
275 def CreateConfigBuffer(self, saved): |
|
276 self.ConfigBuffer = UndoBuffer(cPickle.dumps(self.Config), saved) |
|
277 |
|
278 def BufferConfig(self): |
|
279 self.ConfigBuffer.Buffering(cPickle.dumps(self.Config)) |
|
280 |
|
281 def ConfigIsSaved(self): |
|
282 if self.ConfigBuffer is not None: |
|
283 return self.ConfigBuffer.IsCurrentSaved() |
|
284 else: |
|
285 return True |
|
286 |
|
287 def LoadPrevious(self): |
|
288 self.Config = cPickle.loads(self.ConfigBuffer.Previous()) |
|
289 |
|
290 def LoadNext(self): |
|
291 self.Config = cPickle.loads(self.ConfigBuffer.Next()) |
|
292 |
|
293 def GetBufferState(self): |
|
294 first = self.ConfigBuffer.IsFirst() |
|
295 last = self.ConfigBuffer.IsLast() |
|
296 return not first, not last |
|
297 |
|
298 |
|
299 SLAVE_PDOS_CONFIGURATION_DECLARATION = """ |
|
300 /* Slave %(slave)d, "%(device_type)s" |
|
301 * Vendor ID: 0x%(vendor).8x |
|
302 * Product code: 0x%(product_code).8x |
|
303 * Revision number: 0x%(revision_number).8x |
|
304 */ |
|
305 |
|
306 ec_pdo_entry_info_t slave_%(slave)d_pdo_entries[] = { |
|
307 %(pdos_entries_infos)s |
|
308 }; |
|
309 |
|
310 ec_pdo_info_t slave_%(slave)d_pdos[] = { |
|
311 %(pdos_infos)s |
|
312 }; |
|
313 |
|
314 ec_sync_info_t slave_%(slave)d_syncs[] = { |
|
315 %(pdos_sync_infos)s |
|
316 {0xff} |
|
317 }; |
|
318 """ |
|
319 |
|
320 SLAVE_CONFIGURATION_TEMPLATE = """ |
|
321 if (!(slave%(slave)d = ecrt_master_slave_config(master, %(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x))) { |
|
322 fprintf(stderr, "Failed to get slave %(device_type)s configuration at alias %(alias)d and position %(position)d.\\n"); |
|
323 return -1; |
|
324 } |
|
325 |
|
326 if (ecrt_slave_config_pdos(slave%(slave)d, EC_END, slave_%(slave)d_syncs)) { |
|
327 fprintf(stderr, "Failed to configure PDOs for slave %(device_type)s at alias %(alias)d and position %(position)d.\\n"); |
|
328 return -1; |
|
329 } |
|
330 """ |
|
331 |
|
332 class _EthercatCFileGenerator: |
|
333 |
|
334 def __init__(self, controler, filepath): |
|
335 self.Controler = controler |
|
336 self.FilePath = filepath |
|
337 |
|
338 self.UsedVariables = {} |
|
339 |
|
340 def __del__(self): |
|
341 self.Controler = None |
|
342 |
|
343 def DeclareVariable(self, slave_identifier, index, subindex, iec_type, dir, name): |
|
344 slave_variables = self.UsedVariables.setdefault(slave_identifier, {}) |
|
345 |
|
346 entry_infos = slave_variables.get((index, subindex), None) |
|
347 if entry_infos is None: |
|
348 slave_variables[(index, subindex)] = (iec_type, dir, name) |
|
349 elif entry_infos != (iec_type, dir, name): |
|
350 raise ValueError, _("Definition conflict for location \"%s\"") % name |
|
351 |
|
352 def GenerateCFile(self): |
|
353 |
|
354 current_location = self.Controler.GetCurrentLocation() |
|
355 # define a unique name for the generated C file |
|
356 location_str = "_".join(map(lambda x:str(x), current_location)) |
|
357 |
|
358 plc_etherlab_filepath = os.path.join(os.path.split(__file__)[0], "plc_etherlab.c") |
|
359 plc_etherlab_file = open(plc_etherlab_filepath, 'r') |
|
360 plc_etherlab_code = plc_etherlab_file.read() |
|
361 plc_etherlab_file.close() |
|
362 |
|
363 str_completion = { |
|
364 "location": location_str, |
|
365 "configure_pdos": int(self.Controler.EtherlabNode.getConfigurePDOs()), |
|
366 "master_number": self.Controler.EtherlabNode.getMasterNumber(), |
|
367 "located_variables_declaration": [], |
|
368 "used_pdo_entry_offset_variables_declaration": [], |
|
369 "used_pdo_entry_configuration": [], |
|
370 "pdos_configuration_declaration": "", |
|
371 "slaves_declaration": "", |
|
372 "slaves_configuration": "", |
|
373 "retrieve_variables": [], |
|
374 "publish_variables": [], |
|
375 } |
|
376 |
|
377 for slave_idx, slave_pos in enumerate(self.Controler.GetSlaves()): |
|
378 |
|
379 slave = self.Controler.GetSlave(slave_pos) |
|
380 if slave is not None: |
|
381 type_infos = slave.getType() |
|
382 |
|
383 device = self.Controler.GetModuleInfos(type_infos) |
|
384 if device is not None: |
|
385 |
|
386 pdos = device.getTxPdo() + device.getRxPdo() |
|
387 if len(pdos) > 0: |
|
388 slave_variables = self.UsedVariables.get(slave_pos, {}) |
|
389 |
|
390 for element in ["vendor", "product_code", "revision_number"]: |
|
391 type_infos[element] = ExtractHexDecValue(type_infos[element]) |
|
392 type_infos.update(dict(zip(["slave", "alias", "position"], (slave_idx,) + slave_pos))) |
|
393 |
|
394 str_completion["slaves_declaration"] += "static ec_slave_config_t *slave%(slave)d = NULL;\n" % type_infos |
|
395 str_completion["slaves_configuration"] += SLAVE_CONFIGURATION_TEMPLATE % type_infos |
|
396 |
|
397 pdos_infos = { |
|
398 "pdos_entries_infos": [], |
|
399 "pdos_infos": [], |
|
400 "pdos_sync_infos": [], |
|
401 } |
|
402 pdos_infos.update(type_infos) |
|
403 |
|
404 sync_managers = [] |
|
405 for sync_manager_idx, sync_manager in enumerate(device.getSm()): |
|
406 sync_manager_infos = { |
|
407 "index": sync_manager_idx, |
|
408 "slave": slave_idx, |
|
409 "pdos_number": 0, |
|
410 } |
|
411 |
|
412 sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte()) |
|
413 sync_manager_direction = sync_manager_control_byte & 0x0c |
|
414 sync_manager_watchdog = sync_manager_control_byte & 0x40 |
|
415 if sync_manager_direction: |
|
416 sync_manager_infos["sync_manager_type"] = "EC_DIR_OUTPUT" |
|
417 else: |
|
418 sync_manager_infos["sync_manager_type"] = "EC_DIR_INPUT" |
|
419 if sync_manager_watchdog: |
|
420 sync_manager_infos["watchdog"] = "EC_WD_ENABLE" |
|
421 else: |
|
422 sync_manager_infos["watchdog"] = "EC_WD_DISABLE" |
|
423 |
|
424 sync_managers.append(sync_manager_infos) |
|
425 |
|
426 entry_offset = 0 |
|
427 for pdo in pdos: |
|
428 sync_managers[pdo.getSm()]["pdos_number"] += 1 |
|
429 entries = pdo.getEntry() |
|
430 |
|
431 pdo_infos = { |
|
432 "slave": slave_idx, |
|
433 "index": ExtractHexDecValue(pdo.getIndex().getcontent()), |
|
434 "name": ExtractName(pdo.getName()), |
|
435 "offset": entry_offset, |
|
436 "entries_number": len(entries) |
|
437 } |
|
438 pdos_infos["pdos_infos"].append(" {0x%(index).4x, %(entries_number)d, slave_%(slave)d_pdo_entries + %(offset)d}, /* %(name)s */" % pdo_infos) |
|
439 entry_offset += len(entries) |
|
440 |
|
441 for entry in pdo.getEntry(): |
|
442 index = ExtractHexDecValue(entry.getIndex().getcontent()) |
|
443 subindex = ExtractHexDecValue(entry.getSubIndex()) |
|
444 entry_infos = { |
|
445 "index": index, |
|
446 "subindex": subindex, |
|
447 "name": ExtractName(entry.getName()), |
|
448 "bitlen": entry.getBitLen(), |
|
449 } |
|
450 entry_infos.update(type_infos) |
|
451 pdos_infos["pdos_entries_infos"].append(" {0x%(index).4x, 0x%(subindex).2x, %(bitlen)d}, /* %(name)s */" % entry_infos) |
|
452 |
|
453 entry_declaration = slave_variables.get((index, subindex), None) |
|
454 if entry_declaration is not None: |
|
455 entry_infos.update(dict(zip(["var_type", "dir", "var_name"], entry_declaration))) |
|
456 |
|
457 if entry_infos["var_type"] != entry.getDataType().getcontent(): |
|
458 raise ValueError, _("Wrong type for location \"%s\"!") % entry_infos["var_name"] |
|
459 |
|
460 data_type = DATATYPECONVERSION.get(entry_infos["var_type"], None) |
|
461 if data_type is None: |
|
462 raise ValueError, _("Type of location \"%s\" not yet supported!") % entry_infos["var_name"] |
|
463 |
|
464 if (entry_infos["dir"] == "I" and sync_managers[pdo.getSm()]["sync_manager_type"] != "EC_DIR_INPUT" or |
|
465 entry_infos["dir"] == "Q" and sync_managers[pdo.getSm()]["sync_manager_type"] != "EC_DIR_OUTPUT"): |
|
466 raise ValueError, _("Wrong direction for location \"%s\"!") % entry_infos["var_name"] |
|
467 |
|
468 str_completion["located_variables_declaration"].extend(["IEC_%(var_type)s beremiz%(var_name)s;" % entry_infos, |
|
469 "IEC_%(var_type)s *%(var_name)s = &beremiz%(var_name)s;" % entry_infos]) |
|
470 if data_type == "BIT": |
|
471 str_completion["used_pdo_entry_offset_variables_declaration"].extend(["static unsigned int slave%(slave)d_%(index).4x_%(subindex).2x;" % entry_infos, |
|
472 "static unsigned int slave%(slave)d_%(index).4x_%(subindex).2x_bit;" % entry_infos]) |
|
473 |
|
474 str_completion["used_pdo_entry_configuration"].append(" {%(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x, 0x%(index).4x, %(subindex)d, &slave%(slave)d_%(index).4x_%(subindex).2x, &slave%(slave)d_%(index).4x_%(subindex).2x_bit}," % entry_infos) |
|
475 |
|
476 if entry_infos["dir"] == "I": |
|
477 str_completion["retrieve_variables"].append(" beremiz%(name)s = EC_READ_BIT(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, slave%(slave)d_%(index).4x_%(subindex).2x_bit);" % entry_infos) |
|
478 elif entry_infos["dir"] == "Q": |
|
479 str_completion["publish_variables"].append(" EC_WRITE_BIT(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, slave%(slave)d_%(index).4x_%(subindex).2x_bit, beremiz%(var_name)s);" % entry_infos) |
|
480 |
|
481 else: |
|
482 entry_infos["data_type"] = data_type |
|
483 |
|
484 str_completion["used_pdo_entry_offset_variables_declaration"].append("static unsigned int slave%(slave)d_%(index).4x_%(subindex).2x;" % entry_infos) |
|
485 |
|
486 str_completion["used_pdo_entry_configuration"].append(" {%(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x, 0x%(index).4x, %(subindex)d, &slave%(slave)d_%(index).4x_%(subindex).2x}," % entry_infos) |
|
487 |
|
488 if entry_infos["dir"] == "I": |
|
489 str_completion["retrieve_variables"].append(" beremiz%(name)s = EC_READ_BIT(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x);" % entry_infos) |
|
490 elif entry_infos["dir"] == "Q": |
|
491 str_completion["publish_variables"].append(" EC_WRITE_BIT(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, beremiz%(var_name)s);" % entry_infos) |
|
492 |
|
493 pdo_offset = 0 |
|
494 for sync_manager_infos in sync_managers: |
|
495 sync_manager_infos["offset"] = pdo_offset |
|
496 pdos_infos["pdos_sync_infos"].append(" {%(index)d, %(sync_manager_type)s, %(pdos_number)d, slave_%(slave)d_pdos + %(offset)d, %(watchdog)s}," % sync_manager_infos) |
|
497 pdo_offset += sync_manager_infos["pdos_number"] |
|
498 |
|
499 for element in ["pdos_entries_infos", "pdos_infos", "pdos_sync_infos"]: |
|
500 pdos_infos[element] = "\n".join(pdos_infos[element]) |
|
501 |
|
502 str_completion["pdos_configuration_declaration"] += SLAVE_PDOS_CONFIGURATION_DECLARATION % pdos_infos |
|
503 |
|
504 for element in ["used_pdo_entry_offset_variables_declaration", "used_pdo_entry_configuration", "located_variables_declaration", "retrieve_variables", "publish_variables"]: |
|
505 str_completion[element] = "\n".join(str_completion[element]) |
|
506 |
|
507 etherlabfile = open(self.FilePath,'w') |
|
508 etherlabfile.write(plc_etherlab_code % str_completion) |
|
509 etherlabfile.close() |
|
510 |
|
511 #-------------------------------------------------- |
|
512 # Ethercat Plugin |
|
513 #-------------------------------------------------- |
|
514 |
|
515 EtherCATInfoClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATInfo.xsd")) |
|
516 |
|
517 def GroupItemCompare(x, y): |
|
518 if x["type"] == y["type"]: |
|
519 if x["type"] == ETHERCAT_GROUP: |
|
520 return x["order"].__cmp__(y["order"]) |
|
521 else: |
|
522 return x["name"].__cmp__(y["name"]) |
|
523 elif x["type"] == ETHERCAT_GROUP: |
|
524 return -1 |
|
525 return 1 |
|
526 |
|
527 def SortGroupItems(group): |
|
528 for item in group["children"]: |
|
529 if item["type"] == ETHERCAT_GROUP: |
|
530 SortGroupItems(item) |
|
531 group["children"].sort(GroupItemCompare) |
|
532 |
|
533 def ExtractName(names, default=None): |
|
534 if len(names) == 1: |
|
535 return names[0].getcontent() |
|
536 else: |
|
537 for name in names: |
|
538 if name.getLcId() == 1033: |
|
539 return name.getcontent() |
|
540 return default |
|
541 |
|
542 def ExtractPdoInfos(pdo, pdo_type, infos): |
|
543 pdo_index = pdo.getIndex().getcontent() |
|
544 infos["pdos"].append({"Index": pdo_index, |
|
545 "Name": ExtractName(pdo.getName()), |
|
546 "Type": pdo_type}) |
|
547 for entry in pdo.getEntry(): |
|
548 infos["variables"].append({"Index": entry.getIndex().getcontent(), |
|
549 "SubIndex": entry.getSubIndex(), |
|
550 "Name": ExtractName(entry.getName()), |
|
551 "Type": entry.getDataType().getcontent(), |
|
552 "PDO": pdo_index}) |
|
553 |
|
554 class RootClass: |
|
555 |
|
556 PlugChildsTypes = [("EthercatNode",_EthercatPlug,"Ethercat Master")] |
|
557 |
|
558 def __init__(self): |
|
559 self.LoadModulesLibrary() |
|
560 |
|
561 def GetModulesLibraryPath(self): |
|
562 library_path = os.path.join(self.PlugPath(), "modules") |
|
563 if not os.path.exists(library_path): |
|
564 os.mkdir(library_path) |
|
565 return library_path |
|
566 |
|
567 def _ImportModuleLibrary(self): |
|
568 dialog = wx.FileDialog(self.GetPlugRoot().AppFrame, _("Choose an XML file"), os.getcwd(), "", _("XML files (*.xml)|*.xml|All files|*.*"), wx.OPEN) |
|
569 if dialog.ShowModal() == wx.ID_OK: |
|
570 filepath = dialog.GetPath() |
|
571 if os.path.isfile(filepath): |
|
572 shutil.copy(filepath, self.GetModulesLibraryPath()) |
|
573 self.LoadModulesLibrary() |
|
574 else: |
|
575 self.GetPlugRoot().logger.write_error(_("No such XML file: %s\n") % filepath) |
|
576 dialog.Destroy() |
|
577 |
|
578 PluginMethods = [ |
|
579 {"bitmap" : os.path.join("images", "ImportDEF"), |
|
580 "name" : _("Import module library"), |
|
581 "tooltip" : _("Import module library"), |
|
582 "method" : "_ImportModuleLibrary"}, |
|
583 ] |
|
584 |
|
585 def LoadModulesLibrary(self): |
|
586 self.ModulesLibrary = {} |
|
587 |
|
588 library_path = self.GetModulesLibraryPath() |
|
589 |
|
590 files = os.listdir(library_path) |
|
591 for file in files: |
|
592 filepath = os.path.join(library_path, file) |
|
593 if os.path.isfile(filepath) and os.path.splitext(filepath)[-1] == ".xml": |
|
594 xmlfile = open(filepath, 'r') |
|
595 xml_tree = minidom.parse(xmlfile) |
|
596 xmlfile.close() |
|
597 |
|
598 modules_infos = None |
|
599 for child in xml_tree.childNodes: |
|
600 if child.nodeType == xml_tree.ELEMENT_NODE and child.nodeName == "EtherCATInfo": |
|
601 modules_infos = EtherCATInfoClasses["EtherCATInfo.xsd"]["EtherCATInfo"]() |
|
602 modules_infos.loadXMLTree(child, ["xmlns:xsi", "xsi:noNamespaceSchemaLocation"]) |
|
603 |
|
604 if modules_infos is not None: |
|
605 vendor = modules_infos.getVendor() |
|
606 |
|
607 vendor_category = self.ModulesLibrary.setdefault(vendor.getId(), {"name": ExtractName(vendor.getName(), _("Miscellaneous")), |
|
608 "groups": {}}) |
|
609 |
|
610 for group in modules_infos.getDescriptions().getGroups().getGroup(): |
|
611 group_type = group.getType() |
|
612 |
|
613 vendor_category["groups"].setdefault(group_type, {"name": ExtractName(group.getName(), group_type), |
|
614 "parent": group.getParentGroup(), |
|
615 "order": group.getSortOrder(), |
|
616 "devices": []}) |
|
617 |
|
618 for device in modules_infos.getDescriptions().getDevices().getDevice(): |
|
619 device_group = device.getGroupType() |
|
620 if not vendor_category["groups"].has_key(device_group): |
|
621 raise ValueError, "Not such group \"%\"" % device_group |
|
622 vendor_category["groups"][device_group]["devices"].append((device.getType().getcontent(), device)) |
|
623 |
|
624 def GetModulesLibrary(self): |
|
625 library = [] |
|
626 children_dict = {} |
|
627 for vendor_id, vendor in self.ModulesLibrary.iteritems(): |
|
628 groups = [] |
|
629 library.append({"name": vendor["name"], |
|
630 "type": ETHERCAT_VENDOR, |
|
631 "children": groups}) |
|
632 for group_type, group in vendor["groups"].iteritems(): |
|
633 group_infos = {"name": group["name"], |
|
634 "order": group["order"], |
|
635 "type": ETHERCAT_GROUP, |
|
636 "children": children_dict.setdefault(group_type, [])} |
|
637 if group["parent"] is not None: |
|
638 parent_children = children_dict.setdefault(group["parent"], []) |
|
639 parent_children.append(group_infos) |
|
640 else: |
|
641 groups.append(group_infos) |
|
642 device_dict = {} |
|
643 for device_type, device in group["devices"]: |
|
644 device_infos = {"name": ExtractName(device.getName()), |
|
645 "type": ETHERCAT_DEVICE, |
|
646 "infos": {"device_type": device_type, |
|
647 "vendor": vendor_id, |
|
648 "product_code": device.getType().getProductCode(), |
|
649 "revision_number": device.getType().getRevisionNo()}} |
|
650 group_infos["children"].append(device_infos) |
|
651 device_type_occurrences = device_dict.setdefault(device_type, []) |
|
652 device_type_occurrences.append(device_infos) |
|
653 for device_type_occurrences in device_dict.itervalues(): |
|
654 if len(device_type_occurrences) > 1: |
|
655 for occurrence in device_type_occurrences: |
|
656 occurrence["name"] += _(" (rev. %s)") % occurrence["infos"]["revision_number"] |
|
657 library.sort(lambda x, y: x["name"].__cmp__(y["name"])) |
|
658 return library |
|
659 |
|
660 def GetModuleInfos(self, type_infos): |
|
661 vendor = self.ModulesLibrary.get(type_infos["vendor"], None) |
|
662 if vendor is not None: |
|
663 for group_name, group in vendor["groups"].iteritems(): |
|
664 for device_type, device in group["devices"]: |
|
665 product_code = device.getType().getProductCode() |
|
666 revision_number = device.getType().getRevisionNo() |
|
667 if (device_type == type_infos["device_type"] and |
|
668 product_code == type_infos["product_code"] and |
|
669 revision_number == type_infos["revision_number"]): |
|
670 return device |
|
671 return None |
|
672 |