1 import os, shutil |
1 import os, shutil |
2 import cPickle |
|
3 from xml.dom import minidom |
2 from xml.dom import minidom |
4 |
3 |
5 import wx |
4 import wx |
6 import csv |
5 import csv |
7 |
6 |
8 from xmlclass import * |
7 from xmlclass import * |
9 from POULibrary import POULibrary |
8 |
10 from ConfigTreeNode import ConfigTreeNode |
9 from ConfigTreeNode import ConfigTreeNode |
11 from PLCControler import UndoBuffer, LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY |
10 from PLCControler import UndoBuffer, LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY |
12 from ConfigEditor import NodeEditor, CIA402NodeEditor, MasterEditor, LibraryEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE |
11 |
13 from dialogs import BrowseValuesLibraryDialog |
12 from EthercatSlave import ExtractHexDecValue, ExtractName |
14 from IDEFrame import TITLE, FILEMENU, PROJECTTREE |
13 from EthercatMaster import _EthercatCTN |
15 |
14 from ConfigEditor import LibraryEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE |
16 try: |
|
17 from MotionLibrary import Headers, AxisXSD |
|
18 HAS_MCL = True |
|
19 except: |
|
20 HAS_MCL = False |
|
21 |
|
22 TYPECONVERSION = {"BOOL" : "X", "SINT" : "B", "INT" : "W", "DINT" : "D", "LINT" : "L", |
|
23 "USINT" : "B", "UINT" : "W", "UDINT" : "D", "ULINT" : "L", |
|
24 "BYTE" : "B", "WORD" : "W", "DWORD" : "D", "LWORD" : "L"} |
|
25 |
|
26 DATATYPECONVERSION = {"BOOL" : "BIT", "SINT" : "S8", "INT" : "S16", "DINT" : "S32", "LINT" : "S64", |
|
27 "USINT" : "U8", "UINT" : "U16", "UDINT" : "U32", "ULINT" : "U64", |
|
28 "BYTE" : "U8", "WORD" : "U16", "DWORD" : "U32", "LWORD" : "U64"} |
|
29 |
|
30 VARCLASSCONVERSION = {"T": LOCATION_VAR_INPUT, "R": LOCATION_VAR_OUTPUT, "RT": LOCATION_VAR_MEMORY} |
|
31 |
|
32 #-------------------------------------------------- |
|
33 # Remote Exec Etherlab Commands |
|
34 #-------------------------------------------------- |
|
35 |
|
36 SCAN_COMMAND = """ |
|
37 import commands |
|
38 result = commands.getoutput("ethercat slaves") |
|
39 slaves = [] |
|
40 for slave_line in result.splitlines(): |
|
41 chunks = slave_line.split() |
|
42 idx, pos, state, flag = chunks[:4] |
|
43 name = " ".join(chunks[4:]) |
|
44 alias, position = pos.split(":") |
|
45 slave = {"idx": int(idx), |
|
46 "alias": int(alias), |
|
47 "position": int(position), |
|
48 "name": name} |
|
49 details = commands.getoutput("ethercat slaves -p %d -v" % slave["idx"]) |
|
50 for details_line in details.splitlines(): |
|
51 details_line = details_line.strip() |
|
52 for header, param in [("Vendor Id:", "vendor_id"), |
|
53 ("Product code:", "product_code"), |
|
54 ("Revision number:", "revision_number")]: |
|
55 if details_line.startswith(header): |
|
56 slave[param] = details_line.split()[-1] |
|
57 break |
|
58 slaves.append(slave) |
|
59 returnVal = slaves |
|
60 """ |
|
61 |
|
62 #-------------------------------------------------- |
|
63 # Etherlab Specific Blocks Library |
|
64 #-------------------------------------------------- |
|
65 |
|
66 def GetLocalPath(filename): |
|
67 return os.path.join(os.path.split(__file__)[0], filename) |
|
68 |
|
69 class EtherlabLibrary(POULibrary): |
|
70 def GetLibraryPath(self): |
|
71 return GetLocalPath("pous.xml") |
|
72 |
|
73 def Generate_C(self, buildpath, varlist, IECCFLAGS): |
|
74 etherlab_ext_file = open(GetLocalPath("etherlab_ext.c"), 'r') |
|
75 etherlab_ext_code = etherlab_ext_file.read() |
|
76 etherlab_ext_file.close() |
|
77 |
|
78 Gen_etherlabfile_path = os.path.join(buildpath, "etherlab_ext.c") |
|
79 ethelabfile = open(Gen_etherlabfile_path,'w') |
|
80 ethelabfile.write(etherlab_ext_code) |
|
81 ethelabfile.close() |
|
82 |
|
83 runtimefile_path = os.path.join(os.path.split(__file__)[0], "runtime_etherlab.py") |
|
84 return ((["etherlab_ext"], [(Gen_etherlabfile_path, IECCFLAGS)], True), "", |
|
85 ("runtime_etherlab.py", file(GetLocalPath("runtime_etherlab.py")))) |
|
86 |
|
87 #-------------------------------------------------- |
|
88 # Ethercat Node |
|
89 #-------------------------------------------------- |
|
90 |
|
91 class _EthercatSlaveCTN: |
|
92 |
|
93 NODE_PROFILE = None |
|
94 EditorType = NodeEditor |
|
95 |
|
96 def GetIconName(self): |
|
97 return "Slave" |
|
98 |
|
99 def ExtractHexDecValue(self, value): |
|
100 return ExtractHexDecValue(value) |
|
101 |
|
102 def GetSizeOfType(self, type): |
|
103 return TYPECONVERSION.get(self.GetCTRoot().GetBaseType(type), None) |
|
104 |
|
105 def GetSlavePos(self): |
|
106 return self.BaseParams.getIEC_Channel() |
|
107 |
|
108 def GetParamsAttributes(self, path = None): |
|
109 if path: |
|
110 parts = path.split(".", 1) |
|
111 if self.MandatoryParams and parts[0] == self.MandatoryParams[0]: |
|
112 return self.MandatoryParams[1].getElementInfos(parts[0], parts[1]) |
|
113 elif self.CTNParams and parts[0] == self.CTNParams[0]: |
|
114 return self.CTNParams[1].getElementInfos(parts[0], parts[1]) |
|
115 else: |
|
116 params = [] |
|
117 if self.CTNParams: |
|
118 params.append(self.CTNParams[1].getElementInfos(self.CTNParams[0])) |
|
119 else: |
|
120 params.append({ |
|
121 'use': 'required', |
|
122 'type': 'element', |
|
123 'name': 'SlaveParams', |
|
124 'value': None, |
|
125 'children': [] |
|
126 }) |
|
127 |
|
128 slave_type = self.CTNParent.GetSlaveType(self.GetSlavePos()) |
|
129 params[0]['children'].insert(0, |
|
130 {'use': 'optional', |
|
131 'type': self.CTNParent.GetSlaveTypesLibrary(self.NODE_PROFILE), |
|
132 'name': 'Type', |
|
133 'value': (slave_type["device_type"], slave_type)}) |
|
134 params[0]['children'].insert(1, |
|
135 {'use': 'optional', |
|
136 'type': 'unsignedLong', |
|
137 'name': 'Alias', |
|
138 'value': self.CTNParent.GetSlaveAlias(self.GetSlavePos())}) |
|
139 return params |
|
140 |
|
141 def SetParamsAttribute(self, path, value): |
|
142 position = self.BaseParams.getIEC_Channel() |
|
143 |
|
144 if path == "SlaveParams.Type": |
|
145 self.CTNParent.SetSlaveType(position, value) |
|
146 slave_type = self.CTNParent.GetSlaveType(self.GetSlavePos()) |
|
147 value = (slave_type["device_type"], slave_type) |
|
148 if self._View is not None: |
|
149 wx.CallAfter(self._View.RefreshSlaveInfos) |
|
150 return value, True |
|
151 elif path == "SlaveParams.Alias": |
|
152 self.CTNParent.SetSlaveAlias(position, value) |
|
153 return value, True |
|
154 |
|
155 value, refresh = ConfigTreeNode.SetParamsAttribute(self, path, value) |
|
156 |
|
157 # Filter IEC_Channel, Slave_Type and Alias that have specific behavior |
|
158 if path == "BaseParams.IEC_Channel" and value != position: |
|
159 self.CTNParent.SetSlavePosition(position, value) |
|
160 |
|
161 return value, refresh |
|
162 |
|
163 def GetSlaveInfos(self): |
|
164 return self.CTNParent.GetSlaveInfos(self.GetSlavePos()) |
|
165 |
|
166 def GetSlaveVariables(self, limits): |
|
167 return self.CTNParent.GetSlaveVariables(self.GetSlavePos(), limits) |
|
168 |
|
169 def GetVariableLocationTree(self): |
|
170 return {"name": self.BaseParams.getName(), |
|
171 "type": LOCATION_CONFNODE, |
|
172 "location": self.GetFullIEC_Channel(), |
|
173 "children": self.CTNParent.GetDeviceLocationTree(self.GetSlavePos(), self.GetCurrentLocation(), self.BaseParams.getName()) |
|
174 } |
|
175 |
|
176 def CTNGenerate_C(self, buildpath, locations): |
|
177 return [],"",False |
|
178 |
|
179 #-------------------------------------------------- |
|
180 # Ethercat CIA402 Node |
|
181 #-------------------------------------------------- |
|
182 |
|
183 if HAS_MCL: |
|
184 |
|
185 NODE_VARIABLES = [ |
|
186 ("ControlWord", 0x6040, 0x00, "UINT", "Q"), |
|
187 ("TargetPosition", 0x607a, 0x00, "DINT", "Q"), |
|
188 ("ModesOfOperation", 0x06060, 0x00, "SINT", "Q"), |
|
189 ("StatusWord", 0x6041, 0x00, "UINT", "I"), |
|
190 ("ModesOfOperationDisplay", 0x06061, 0x00, "SINT", "I"), |
|
191 ("ActualPosition", 0x6064, 0x00, "DINT", "I"), |
|
192 ("ActualVelocity", 0x606C, 0x00, "DINT", "I"), |
|
193 ] |
|
194 |
|
195 DEFAULT_RETRIEVE = " __CIA402Node_%(location)s.axis->%(name)s = *(__CIA402Node_%(location)s.%(name)s);" |
|
196 DEFAULT_PUBLISH = " *(__CIA402Node_%(location)s.%(name)s) = __CIA402Node_%(location)s.axis->%(name)s;" |
|
197 |
|
198 EXTRA_NODE_VARIABLES = [ |
|
199 ("ErrorCode", [ |
|
200 {"description": ("ErrorCode", 0x603F, 0x00, "UINT", "I"), |
|
201 "publish": None} |
|
202 ]), |
|
203 ("DigitalInputs", [ |
|
204 {"description": ("DigitalInputs", 0x60FD, 0x00, "UDINT", "I"), |
|
205 "publish": None} |
|
206 ]), |
|
207 ("DigitalOutputs", [ |
|
208 {"description": ("DigitalOutputs", 0x60FE, 0x00, "UDINT", "Q"), |
|
209 "retrieve": None} |
|
210 ]) |
|
211 ] |
|
212 EXTRA_NODE_VARIABLES_DICT = dict([("Enable" + name, value) for name, value in EXTRA_NODE_VARIABLES]) |
|
213 |
|
214 BLOCK_INPUT_TEMPLATE = " __SET_VAR(%(blockname)s->,%(input_name)s, %(input_value)s);" |
|
215 BLOCK_OUTPUT_TEMPLATE = " __SET_VAR(data__->,%(output_name)s, __GET_VAR(%(blockname)s->%(output_name)s));" |
|
216 |
|
217 BLOCK_FUNCTION_TEMPLATE = """ |
|
218 extern void ETHERLAB%(ucase_blocktype)s_body__(ETHERLAB%(ucase_blocktype)s* data__); |
|
219 void __%(blocktype)s_%(location)s(MC_%(ucase_blocktype)s *data__) { |
|
220 __DECLARE_GLOBAL_PROTOTYPE(ETHERLAB%(ucase_blocktype)s, %(blockname)s); |
|
221 ETHERLAB%(ucase_blocktype)s* %(blockname)s = __GET_GLOBAL_%(blockname)s(); |
|
222 %(extract_inputs)s |
|
223 ETHERLAB%(ucase_blocktype)s_body__(%(blockname)s); |
|
224 %(return_outputs)s |
|
225 } |
|
226 """ |
|
227 |
|
228 BLOCK_FUNTION_DEFINITION_TEMPLATE = """ if (!__CIA402Node_%(location)s.axis->__mcl_func_MC_%(blocktype)s) |
|
229 __CIA402Node_%(location)s.axis->__mcl_func_MC_%(blocktype)s = __%(blocktype)s_%(location)s;""" |
|
230 |
|
231 GLOBAL_INSTANCES = [ |
|
232 {"blocktype": "GetTorqueLimit", |
|
233 "inputs": [], |
|
234 "outputs": [{"name": "TorqueLimitPos", "type": "UINT"}, |
|
235 {"name": "TorqueLimitNeg", "type": "UINT"}]}, |
|
236 {"blocktype": "SetTorqueLimit", |
|
237 "inputs": [{"name": "TorqueLimitPos", "type": "UINT"}, |
|
238 {"name": "TorqueLimitNeg", "type": "UINT"}], |
|
239 "outputs": []}, |
|
240 ] |
|
241 |
|
242 class _EthercatCIA402SlaveCTN(_EthercatSlaveCTN): |
|
243 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
|
244 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
|
245 <xsd:element name="CIA402SlaveParams"> |
|
246 <xsd:complexType> |
|
247 %s |
|
248 </xsd:complexType> |
|
249 </xsd:element> |
|
250 </xsd:schema> |
|
251 """ % ("\n".join(['<xsd:attribute name="Enable%s" type="xsd:boolean" use="optional" default="false"/>' % category |
|
252 for category, variables in EXTRA_NODE_VARIABLES]) + AxisXSD) |
|
253 |
|
254 NODE_PROFILE = 402 |
|
255 EditorType = CIA402NodeEditor |
|
256 |
|
257 ConfNodeMethods = [ |
|
258 {"bitmap" : "CIA402AxisRef", |
|
259 "name" : _("Axis Ref"), |
|
260 "tooltip" : _("Initiate Drag'n drop of Axis ref located variable"), |
|
261 "method" : "_getCIA402AxisRef", |
|
262 "push": True}, |
|
263 ] |
|
264 |
|
265 def GetIconName(self): |
|
266 return "CIA402Slave" |
|
267 |
|
268 def SetParamsAttribute(self, path, value): |
|
269 if path == "CIA402SlaveParams.Type": |
|
270 path = "SlaveParams.Type" |
|
271 elif path == "CIA402SlaveParams.Alias": |
|
272 path = "SlaveParams.Alias" |
|
273 return _EthercatSlaveCTN.SetParamsAttribute(self, path, value) |
|
274 |
|
275 def GetVariableLocationTree(self): |
|
276 axis_name = self.CTNName() |
|
277 current_location = self.GetCurrentLocation() |
|
278 children = [{"name": "%s Axis Ref" % (axis_name), |
|
279 "type": LOCATION_VAR_INPUT, |
|
280 "size": "W", |
|
281 "IEC_type": "AXIS_REF", |
|
282 "var_name": axis_name, |
|
283 "location": "%%IW%s.0" % (".".join(map(str, current_location))), |
|
284 "description": "", |
|
285 "children": []}] |
|
286 children.extend(self.CTNParent.GetDeviceLocationTree(self.GetSlavePos(), current_location, axis_name)) |
|
287 return {"name": axis_name, |
|
288 "type": LOCATION_CONFNODE, |
|
289 "location": self.GetFullIEC_Channel(), |
|
290 "children": children, |
|
291 } |
|
292 |
|
293 def CTNGlobalInstances(self): |
|
294 current_location = self.GetCurrentLocation() |
|
295 return [("%s_%s" % (block_infos["blocktype"], "_".join(map(str, current_location))), |
|
296 "EtherLab%s" % block_infos["blocktype"]) for block_infos in GLOBAL_INSTANCES] |
|
297 |
|
298 def _getCIA402AxisRef(self): |
|
299 data = wx.TextDataObject(str(("%%IW%s.0" % ".".join(map(str, self.GetCurrentLocation())), |
|
300 "location", "AXIS_REF", self.CTNName(), ""))) |
|
301 dragSource = wx.DropSource(self.GetCTRoot().AppFrame) |
|
302 dragSource.SetData(data) |
|
303 dragSource.DoDragDrop() |
|
304 |
|
305 def CTNGenerate_C(self, buildpath, locations): |
|
306 current_location = self.GetCurrentLocation() |
|
307 |
|
308 location_str = "_".join(map(lambda x:str(x), current_location)) |
|
309 |
|
310 plc_cia402node_filepath = os.path.join(os.path.split(__file__)[0], "plc_cia402node.c") |
|
311 plc_cia402node_file = open(plc_cia402node_filepath, 'r') |
|
312 plc_cia402node_code = plc_cia402node_file.read() |
|
313 plc_cia402node_file.close() |
|
314 |
|
315 str_completion = { |
|
316 "slave_pos": self.GetSlavePos(), |
|
317 "location": location_str, |
|
318 "MCL_headers": Headers, |
|
319 "extern_located_variables_declaration": [], |
|
320 "fieldbus_interface_declaration": [], |
|
321 "fieldbus_interface_definition": [], |
|
322 "entry_variables": [], |
|
323 "init_axis_params": [], |
|
324 "init_entry_variables": [], |
|
325 "extra_variables_retrieve": [], |
|
326 "extra_variables_publish": [] |
|
327 } |
|
328 |
|
329 for blocktype_infos in GLOBAL_INSTANCES: |
|
330 texts = { |
|
331 "blocktype": blocktype_infos["blocktype"], |
|
332 "ucase_blocktype": blocktype_infos["blocktype"].upper(), |
|
333 "location": "_".join(map(str, current_location)) |
|
334 } |
|
335 texts["blockname"] = "%(ucase_blocktype)s_%(location)s" % texts |
|
336 |
|
337 inputs = [{"input_name": "POS", "input_value": str(self.GetSlavePos())}, |
|
338 {"input_name": "EXECUTE", "input_value": "__GET_VAR(data__->EXECUTE)"}] +\ |
|
339 [{"input_name": input["name"].upper(), |
|
340 "input_value": "__GET_VAR(data__->%s)" % input["name"].upper()} |
|
341 for input in blocktype_infos["inputs"]] |
|
342 input_texts = [] |
|
343 for input_infos in inputs: |
|
344 input_infos.update(texts) |
|
345 input_texts.append(BLOCK_INPUT_TEMPLATE % input_infos) |
|
346 texts["extract_inputs"] = "\n".join(input_texts) |
|
347 |
|
348 outputs = [{"output_name": output} for output in ["DONE", "BUSY", "ERROR"]] + \ |
|
349 [{"output_name": output["name"].upper()} for output in blocktype_infos["outputs"]] |
|
350 output_texts = [] |
|
351 for output_infos in outputs: |
|
352 output_infos.update(texts) |
|
353 output_texts.append(BLOCK_OUTPUT_TEMPLATE % output_infos) |
|
354 texts["return_outputs"] = "\n".join(output_texts) |
|
355 |
|
356 str_completion["fieldbus_interface_declaration"].append( |
|
357 BLOCK_FUNCTION_TEMPLATE % texts) |
|
358 |
|
359 str_completion["fieldbus_interface_definition"].append( |
|
360 BLOCK_FUNTION_DEFINITION_TEMPLATE % texts) |
|
361 |
|
362 variables = NODE_VARIABLES[:] |
|
363 |
|
364 params = self.CTNParams[1].getElementInfos(self.CTNParams[0]) |
|
365 for param in params["children"]: |
|
366 if param["name"] in EXTRA_NODE_VARIABLES_DICT: |
|
367 if param["value"]: |
|
368 extra_variables = EXTRA_NODE_VARIABLES_DICT.get(param["name"]) |
|
369 for variable_infos in extra_variables: |
|
370 var_infos = { |
|
371 "location": location_str, |
|
372 "name": variable_infos["description"][0] |
|
373 } |
|
374 variables.append(variable_infos["description"]) |
|
375 retrieve_template = variable_infos.get("retrieve", DEFAULT_RETRIEVE) |
|
376 publish_template = variable_infos.get("publish", DEFAULT_PUBLISH) |
|
377 |
|
378 if retrieve_template is not None: |
|
379 str_completion["extra_variables_retrieve"].append( |
|
380 retrieve_template % var_infos) |
|
381 if publish_template is not None: |
|
382 str_completion["extra_variables_publish"].append( |
|
383 publish_template % var_infos) |
|
384 elif param["value"] is not None: |
|
385 param_infos = { |
|
386 "location": location_str, |
|
387 "param_name": param["name"], |
|
388 } |
|
389 if param["type"] == "boolean": |
|
390 param_infos["param_value"] = {True: "true", False: "false"}[param["value"]] |
|
391 else: |
|
392 param_infos["param_value"] = str(param["value"]) |
|
393 str_completion["init_axis_params"].append( |
|
394 " __CIA402Node_%(location)s.axis->%(param_name)s = %(param_value)s;" % param_infos) |
|
395 |
|
396 for variable in variables: |
|
397 var_infos = dict(zip(["name", "index", "subindex", "var_type", "dir"], variable)) |
|
398 var_infos["location"] = location_str |
|
399 var_infos["var_size"] = self.GetSizeOfType(var_infos["var_type"]) |
|
400 var_infos["var_name"] = "__%(dir)s%(var_size)s%(location)s_%(index)d_%(subindex)d" % var_infos |
|
401 |
|
402 str_completion["extern_located_variables_declaration"].append( |
|
403 "IEC_%(var_type)s *%(var_name)s;" % var_infos) |
|
404 str_completion["entry_variables"].append( |
|
405 " IEC_%(var_type)s *%(name)s;" % var_infos) |
|
406 str_completion["init_entry_variables"].append( |
|
407 " __CIA402Node_%(location)s.%(name)s = %(var_name)s;" % var_infos) |
|
408 |
|
409 self.CTNParent.FileGenerator.DeclareVariable( |
|
410 self.GetSlavePos(), var_infos["index"], var_infos["subindex"], |
|
411 var_infos["var_type"], var_infos["dir"], var_infos["var_name"]) |
|
412 |
|
413 for element in ["extern_located_variables_declaration", |
|
414 "fieldbus_interface_declaration", |
|
415 "fieldbus_interface_definition", |
|
416 "entry_variables", |
|
417 "init_axis_params", |
|
418 "init_entry_variables", |
|
419 "extra_variables_retrieve", |
|
420 "extra_variables_publish"]: |
|
421 str_completion[element] = "\n".join(str_completion[element]) |
|
422 |
|
423 Gen_CIA402Nodefile_path = os.path.join(buildpath, "cia402node_%s.c"%location_str) |
|
424 cia402nodefile = open(Gen_CIA402Nodefile_path, 'w') |
|
425 cia402nodefile.write(plc_cia402node_code % str_completion) |
|
426 cia402nodefile.close() |
|
427 |
|
428 return [(Gen_CIA402Nodefile_path, '"-I%s"'%os.path.abspath(self.GetCTRoot().GetIECLibPath()))],"",True |
|
429 |
|
430 #-------------------------------------------------- |
|
431 # Ethercat MASTER |
|
432 #-------------------------------------------------- |
|
433 |
|
434 EtherCATConfigClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATConfig.xsd")) |
|
435 |
|
436 def ExtractHexDecValue(value): |
|
437 try: |
|
438 return int(value) |
|
439 except: |
|
440 pass |
|
441 try: |
|
442 return int(value.replace("#", "0"), 16) |
|
443 except: |
|
444 raise ValueError, "Invalid value for HexDecValue \"%s\"" % value |
|
445 |
|
446 def GenerateHexDecValue(value, base=10): |
|
447 if base == 10: |
|
448 return str(value) |
|
449 elif base == 16: |
|
450 return "#x%.8x" % value |
|
451 else: |
|
452 raise ValueError, "Not supported base" |
|
453 |
|
454 def sort_commands(x, y): |
|
455 if x["Index"] == y["Index"]: |
|
456 return cmp(x["Subindex"], y["Subindex"]) |
|
457 return cmp(x["Index"], y["Index"]) |
|
458 |
|
459 cls = EtherCATConfigClasses.get("Config_Slave", None) |
|
460 if cls: |
|
461 |
|
462 def getType(self): |
|
463 slave_info = self.getInfo() |
|
464 return {"device_type": slave_info.getName(), |
|
465 "vendor": GenerateHexDecValue(slave_info.getVendorId()), |
|
466 "product_code": GenerateHexDecValue(slave_info.getProductCode(), 16), |
|
467 "revision_number": GenerateHexDecValue(slave_info.getRevisionNo(), 16)} |
|
468 setattr(cls, "getType", getType) |
|
469 |
|
470 def setType(self, type_infos): |
|
471 slave_info = self.getInfo() |
|
472 slave_info.setName(type_infos["device_type"]) |
|
473 slave_info.setVendorId(ExtractHexDecValue(type_infos["vendor"])) |
|
474 slave_info.setProductCode(ExtractHexDecValue(type_infos["product_code"])) |
|
475 slave_info.setRevisionNo(ExtractHexDecValue(type_infos["revision_number"])) |
|
476 setattr(cls, "setType", setType) |
|
477 |
|
478 def getInitCmds(self, create_default=False): |
|
479 Mailbox = self.getMailbox() |
|
480 if Mailbox is None: |
|
481 if create_default: |
|
482 self.addMailbox() |
|
483 Mailbox = self.getMailbox() |
|
484 else: |
|
485 return None |
|
486 CoE = Mailbox.getCoE() |
|
487 if CoE is None: |
|
488 if create_default: |
|
489 Mailbox.addCoE() |
|
490 CoE = Mailbox.getCoE() |
|
491 else: |
|
492 return None |
|
493 InitCmds = CoE.getInitCmds() |
|
494 if InitCmds is None and create_default: |
|
495 CoE.addInitCmds() |
|
496 InitCmds = CoE.getInitCmds() |
|
497 return InitCmds |
|
498 setattr(cls, "getInitCmds", getInitCmds) |
|
499 |
|
500 def getStartupCommands(self): |
|
501 pos = self.getInfo().getPhysAddr() |
|
502 InitCmds = self.getInitCmds() |
|
503 if InitCmds is None: |
|
504 return [] |
|
505 commands = [] |
|
506 for idx, InitCmd in enumerate(InitCmds.getInitCmd()): |
|
507 comment = InitCmd.getComment() |
|
508 if comment is None: |
|
509 comment = "" |
|
510 commands.append({ |
|
511 "command_idx": idx, |
|
512 "Position": pos, |
|
513 "Index": InitCmd.getIndex(), |
|
514 "Subindex": InitCmd.getSubIndex(), |
|
515 "Value": InitCmd.getData(), |
|
516 "Description": comment}) |
|
517 commands.sort(sort_commands) |
|
518 return commands |
|
519 setattr(cls, "getStartupCommands", getStartupCommands) |
|
520 |
|
521 def appendStartupCommand(self, command_infos): |
|
522 InitCmds = self.getInitCmds(True) |
|
523 command = EtherCATConfigClasses["InitCmds_InitCmd"]() |
|
524 command.setIndex(command_infos["Index"]) |
|
525 command.setSubIndex(command_infos["Subindex"]) |
|
526 command.setData(command_infos["Value"]) |
|
527 command.setComment(command_infos["Description"]) |
|
528 InitCmds.appendInitCmd(command) |
|
529 return len(InitCmds.getInitCmd()) - 1 |
|
530 setattr(cls, "appendStartupCommand", appendStartupCommand) |
|
531 |
|
532 def setStartupCommand(self, command_infos): |
|
533 InitCmds = self.getInitCmds() |
|
534 if InitCmds is not None: |
|
535 commands = InitCmds.getInitCmd() |
|
536 if command_infos["command_idx"] < len(commands): |
|
537 command = commands[command_infos["command_idx"]] |
|
538 command.setIndex(command_infos["Index"]) |
|
539 command.setSubIndex(command_infos["Subindex"]) |
|
540 command.setData(command_infos["Value"]) |
|
541 command.setComment(command_infos["Description"]) |
|
542 setattr(cls, "setStartupCommand", setStartupCommand) |
|
543 |
|
544 def removeStartupCommand(self, command_idx): |
|
545 InitCmds = self.getInitCmds() |
|
546 if InitCmds is not None: |
|
547 if command_idx < len(InitCmds.getInitCmd()): |
|
548 InitCmds.removeInitCmd(command_idx) |
|
549 setattr(cls, "removeStartupCommand", removeStartupCommand) |
|
550 |
|
551 ProcessVariablesXSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
|
552 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
|
553 <xsd:element name="ProcessVariables"> |
|
554 <xsd:complexType> |
|
555 <xsd:sequence> |
|
556 <xsd:element name="variable" minOccurs="0" maxOccurs="unbounded"> |
|
557 <xsd:complexType> |
|
558 <xsd:sequence> |
|
559 <xsd:element name="ReadFrom" type="LocationDesc" minOccurs="0"/> |
|
560 <xsd:element name="WriteTo" type="LocationDesc" minOccurs="0"/> |
|
561 </xsd:sequence> |
|
562 <xsd:attribute name="Name" type="xsd:string" use="required"/> |
|
563 <xsd:attribute name="Comment" type="xsd:string" use="required"/> |
|
564 </xsd:complexType> |
|
565 </xsd:element> |
|
566 </xsd:sequence> |
|
567 </xsd:complexType> |
|
568 </xsd:element> |
|
569 <xsd:complexType name="LocationDesc"> |
|
570 <xsd:attribute name="Position" type="xsd:integer" use="required"/> |
|
571 <xsd:attribute name="Index" type="xsd:integer" use="required"/> |
|
572 <xsd:attribute name="SubIndex" type="xsd:integer" use="required"/> |
|
573 </xsd:complexType> |
|
574 </xsd:schema> |
|
575 """ |
|
576 |
|
577 ProcessVariablesClasses = GenerateClassesFromXSDstring(ProcessVariablesXSD) |
|
578 |
|
579 class _EthercatCTN: |
|
580 |
|
581 CTNChildrenTypes = [("EthercatSlave", _EthercatSlaveCTN, "Ethercat Slave")] |
|
582 if HAS_MCL: |
|
583 CTNChildrenTypes.append(("EthercatCIA402Slave", _EthercatCIA402SlaveCTN, "Ethercat CIA402 Slave")) |
|
584 EditorType = MasterEditor |
|
585 |
|
586 def __init__(self): |
|
587 config_filepath = self.ConfigFileName() |
|
588 config_is_saved = False |
|
589 self.Config = EtherCATConfigClasses["EtherCATConfig"]() |
|
590 if os.path.isfile(config_filepath): |
|
591 config_xmlfile = open(config_filepath, 'r') |
|
592 config_tree = minidom.parse(config_xmlfile) |
|
593 config_xmlfile.close() |
|
594 |
|
595 for child in config_tree.childNodes: |
|
596 if child.nodeType == config_tree.ELEMENT_NODE and child.nodeName == "EtherCATConfig": |
|
597 self.Config.loadXMLTree(child) |
|
598 config_is_saved = True |
|
599 |
|
600 process_filepath = self.ProcessVariablesFileName() |
|
601 process_is_saved = False |
|
602 self.ProcessVariables = ProcessVariablesClasses["ProcessVariables"]() |
|
603 if os.path.isfile(process_filepath): |
|
604 process_xmlfile = open(process_filepath, 'r') |
|
605 process_tree = minidom.parse(process_xmlfile) |
|
606 process_xmlfile.close() |
|
607 |
|
608 for child in process_tree.childNodes: |
|
609 if child.nodeType == process_tree.ELEMENT_NODE and child.nodeName == "ProcessVariables": |
|
610 self.ProcessVariables.loadXMLTree(child) |
|
611 process_is_saved = True |
|
612 |
|
613 if config_is_saved and process_is_saved: |
|
614 self.CreateBuffer(True) |
|
615 else: |
|
616 self.CreateBuffer(False) |
|
617 self.OnCTNSave() |
|
618 |
|
619 def GetContextualMenuItems(self): |
|
620 return [("Add Ethercat Slave", "Add Ethercat Slave to Master", self.OnAddEthercatSlave)] |
|
621 |
|
622 def OnAddEthercatSlave(self, event): |
|
623 app_frame = self.GetCTRoot().AppFrame |
|
624 dialog = BrowseValuesLibraryDialog(app_frame, |
|
625 "Ethercat Slave Type", self.GetSlaveTypesLibrary()) |
|
626 if dialog.ShowModal() == wx.ID_OK: |
|
627 type_infos = dialog.GetValueInfos() |
|
628 device, alignment = self.GetModuleInfos(type_infos) |
|
629 if device is not None: |
|
630 if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers(): |
|
631 ConfNodeType = "EthercatCIA402Slave" |
|
632 else: |
|
633 ConfNodeType = "EthercatSlave" |
|
634 new_child = self.CTNAddChild("%s_0" % ConfNodeType, ConfNodeType) |
|
635 new_child.SetParamsAttribute("SlaveParams.Type", type_infos) |
|
636 self.CTNRequestSave() |
|
637 new_child._OpenView() |
|
638 app_frame._Refresh(TITLE, FILEMENU, PROJECTTREE) |
|
639 dialog.Destroy() |
|
640 |
|
641 def ExtractHexDecValue(self, value): |
|
642 return ExtractHexDecValue(value) |
|
643 |
|
644 def GetSizeOfType(self, type): |
|
645 return TYPECONVERSION.get(self.GetCTRoot().GetBaseType(type), None) |
|
646 |
|
647 def ConfigFileName(self): |
|
648 return os.path.join(self.CTNPath(), "config.xml") |
|
649 |
|
650 def ProcessVariablesFileName(self): |
|
651 return os.path.join(self.CTNPath(), "process_variables.xml") |
|
652 |
|
653 def FilterSlave(self, slave, vendor=None, slave_pos=None, slave_profile=None): |
|
654 if slave_pos is not None and slave.getInfo().getPhysAddr() != slave_pos: |
|
655 return False |
|
656 type_infos = slave.getType() |
|
657 if vendor is not None and ExtractHexDecValue(type_infos["vendor"]) != vendor: |
|
658 return False |
|
659 device, alignment = self.GetModuleInfos(type_infos) |
|
660 if slave_profile is not None and slave_profile not in device.GetProfileNumbers(): |
|
661 return False |
|
662 return True |
|
663 |
|
664 def GetSlaves(self, vendor=None, slave_pos=None, slave_profile=None): |
|
665 slaves = [] |
|
666 for slave in self.Config.getConfig().getSlave(): |
|
667 if self.FilterSlave(slave, vendor, slave_pos, slave_profile): |
|
668 slaves.append(slave.getInfo().getPhysAddr()) |
|
669 slaves.sort() |
|
670 return slaves |
|
671 |
|
672 def GetSlave(self, slave_pos): |
|
673 for slave in self.Config.getConfig().getSlave(): |
|
674 slave_info = slave.getInfo() |
|
675 if slave_info.getPhysAddr() == slave_pos: |
|
676 return slave |
|
677 return None |
|
678 |
|
679 def GetStartupCommands(self, vendor=None, slave_pos=None, slave_profile=None): |
|
680 commands = [] |
|
681 for slave in self.Config.getConfig().getSlave(): |
|
682 if self.FilterSlave(slave, vendor, slave_pos, slave_profile): |
|
683 commands.append((slave.getInfo().getPhysAddr(), slave.getStartupCommands())) |
|
684 commands.sort() |
|
685 return reduce(lambda x, y: x + y[1], commands, []) |
|
686 |
|
687 def AppendStartupCommand(self, command_infos): |
|
688 slave = self.GetSlave(command_infos["Position"]) |
|
689 if slave is not None: |
|
690 command_idx = slave.appendStartupCommand(command_infos) |
|
691 self.BufferModel() |
|
692 return command_idx |
|
693 return None |
|
694 |
|
695 def SetStartupCommandInfos(self, command_infos): |
|
696 slave = self.GetSlave(command_infos["Position"]) |
|
697 if slave is not None: |
|
698 slave.setStartupCommand(command_infos) |
|
699 self.BufferModel() |
|
700 |
|
701 def RemoveStartupCommand(self, slave_pos, command_idx, buffer=True): |
|
702 slave = self.GetSlave(slave_pos) |
|
703 if slave is not None: |
|
704 slave.removeStartupCommand(command_idx) |
|
705 if buffer: |
|
706 self.BufferModel() |
|
707 |
|
708 def SetProcessVariables(self, variables): |
|
709 vars = [] |
|
710 for var in variables: |
|
711 variable = ProcessVariablesClasses["ProcessVariables_variable"]() |
|
712 variable.setName(var["Name"]) |
|
713 variable.setComment(var["Description"]) |
|
714 if var["ReadFrom"] != "": |
|
715 position, index, subindex = var["ReadFrom"] |
|
716 if variable.getReadFrom() is None: |
|
717 variable.addReadFrom() |
|
718 read_from = variable.getReadFrom() |
|
719 read_from.setPosition(position) |
|
720 read_from.setIndex(index) |
|
721 read_from.setSubIndex(subindex) |
|
722 elif variable.getReadFrom() is not None: |
|
723 variable.deleteReadFrom() |
|
724 if var["WriteTo"] != "": |
|
725 position, index, subindex = var["WriteTo"] |
|
726 if variable.getWriteTo() is None: |
|
727 variable.addWriteTo() |
|
728 write_to = variable.getWriteTo() |
|
729 write_to.setPosition(position) |
|
730 write_to.setIndex(index) |
|
731 write_to.setSubIndex(subindex) |
|
732 elif variable.getWriteTo() is not None: |
|
733 variable.deleteWriteTo() |
|
734 vars.append(variable) |
|
735 self.ProcessVariables.setvariable(vars) |
|
736 self.BufferModel() |
|
737 |
|
738 def GetProcessVariables(self): |
|
739 variables = [] |
|
740 idx = 0 |
|
741 for variable in self.ProcessVariables.getvariable(): |
|
742 var = {"Name": variable.getName(), |
|
743 "Number": idx, |
|
744 "Description": variable.getComment()} |
|
745 read_from = variable.getReadFrom() |
|
746 if read_from is not None: |
|
747 var["ReadFrom"] = (read_from.getPosition(), |
|
748 read_from.getIndex(), |
|
749 read_from.getSubIndex()) |
|
750 else: |
|
751 var["ReadFrom"] = "" |
|
752 write_to = variable.getWriteTo() |
|
753 if write_to is not None: |
|
754 var["WriteTo"] = (write_to.getPosition(), |
|
755 write_to.getIndex(), |
|
756 write_to.getSubIndex()) |
|
757 else: |
|
758 var["WriteTo"] = "" |
|
759 variables.append(var) |
|
760 idx += 1 |
|
761 return variables |
|
762 |
|
763 def _ScanNetwork(self): |
|
764 app_frame = self.GetCTRoot().AppFrame |
|
765 |
|
766 execute = True |
|
767 if len(self.Children) > 0: |
|
768 dialog = wx.MessageDialog(app_frame, |
|
769 _("The current network configuration will be deleted.\nDo you want to continue?"), |
|
770 _("Scan Network"), |
|
771 wx.YES_NO|wx.ICON_QUESTION) |
|
772 execute = dialog.ShowModal() == wx.ID_YES |
|
773 dialog.Destroy() |
|
774 |
|
775 if execute: |
|
776 error, returnVal = self.RemoteExec(SCAN_COMMAND, returnVal = None) |
|
777 if error != 0: |
|
778 dialog = wx.MessageDialog(app_frame, returnVal, "Error", wx.OK|wx.ICON_ERROR) |
|
779 dialog.ShowModal() |
|
780 dialog.Destroy() |
|
781 elif returnVal is not None: |
|
782 for child in self.IECSortedChildren(): |
|
783 self._doRemoveChild(child) |
|
784 |
|
785 for slave in returnVal: |
|
786 type_infos = { |
|
787 "vendor": slave["vendor_id"], |
|
788 "product_code": slave["product_code"], |
|
789 "revision_number":slave["revision_number"], |
|
790 } |
|
791 device, alignment = self.GetModuleInfos(type_infos) |
|
792 if device is not None: |
|
793 if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers(): |
|
794 CTNType = "EthercatCIA402Slave" |
|
795 else: |
|
796 CTNType = "EthercatSlave" |
|
797 self.CTNAddChild("slave%s" % slave["idx"], CTNType, slave["idx"]) |
|
798 self.SetSlaveAlias(slave["idx"], slave["alias"]) |
|
799 type_infos["device_type"] = device.getType().getcontent() |
|
800 self.SetSlaveType(slave["idx"], type_infos) |
|
801 |
|
802 def CTNAddChild(self, CTNName, CTNType, IEC_Channel=0): |
|
803 """ |
|
804 Create the confnodes that may be added as child to this node self |
|
805 @param CTNType: string desining the confnode class name (get name from CTNChildrenTypes) |
|
806 @param CTNName: string for the name of the confnode instance |
|
807 """ |
|
808 newConfNodeOpj = ConfigTreeNode.CTNAddChild(self, CTNName, CTNType, IEC_Channel) |
|
809 |
|
810 slave = self.GetSlave(newConfNodeOpj.BaseParams.getIEC_Channel()) |
|
811 if slave is None: |
|
812 slave = EtherCATConfigClasses["Config_Slave"]() |
|
813 slave_infos = slave.getInfo() |
|
814 slave_infos.setName("undefined") |
|
815 slave_infos.setPhysAddr(newConfNodeOpj.BaseParams.getIEC_Channel()) |
|
816 slave_infos.setAutoIncAddr(0) |
|
817 self.Config.getConfig().appendSlave(slave) |
|
818 self.BufferModel() |
|
819 self.OnCTNSave() |
|
820 |
|
821 return newConfNodeOpj |
|
822 |
|
823 def _doRemoveChild(self, CTNInstance): |
|
824 slave_pos = CTNInstance.GetSlavePos() |
|
825 config = self.Config.getConfig() |
|
826 for idx, slave in enumerate(config.getSlave()): |
|
827 slave_infos = slave.getInfo() |
|
828 if slave_infos.getPhysAddr() == slave_pos: |
|
829 config.removeSlave(idx) |
|
830 self.BufferModel() |
|
831 self.OnCTNSave() |
|
832 ConfigTreeNode._doRemoveChild(self, CTNInstance) |
|
833 |
|
834 def SetSlavePosition(self, slave_pos, new_pos): |
|
835 slave = self.GetSlave(slave_pos) |
|
836 if slave is not None: |
|
837 slave_info = slave.getInfo() |
|
838 slave_info.setPhysAddr(new_pos) |
|
839 for variable in self.ProcessVariables.getvariable(): |
|
840 read_from = variable.getReadFrom() |
|
841 if read_from is not None and read_from.getPosition() == slave_pos: |
|
842 read_from.setPosition(new_pos) |
|
843 write_to = variable.getWriteTo() |
|
844 if write_to is not None and write_to.getPosition() == slave_pos: |
|
845 write_to.setPosition(new_pos) |
|
846 self.CreateBuffer(True) |
|
847 self.OnCTNSave() |
|
848 if self._View is not None: |
|
849 self._View.RefreshView() |
|
850 self._View.RefreshBuffer() |
|
851 |
|
852 def GetSlaveAlias(self, slave_pos): |
|
853 slave = self.GetSlave(slave_pos) |
|
854 if slave is not None: |
|
855 slave_info = slave.getInfo() |
|
856 return slave_info.getAutoIncAddr() |
|
857 return None |
|
858 |
|
859 def SetSlaveAlias(self, slave_pos, alias): |
|
860 slave = self.GetSlave(slave_pos) |
|
861 if slave is not None: |
|
862 slave_info = slave.getInfo() |
|
863 slave_info.setAutoIncAddr(alias) |
|
864 self.BufferModel() |
|
865 |
|
866 def GetSlaveType(self, slave_pos): |
|
867 slave = self.GetSlave(slave_pos) |
|
868 if slave is not None: |
|
869 return slave.getType() |
|
870 return None |
|
871 |
|
872 def SetSlaveType(self, slave_pos, type_infos): |
|
873 slave = self.GetSlave(slave_pos) |
|
874 if slave is not None: |
|
875 slave.setType(type_infos) |
|
876 self.BufferModel() |
|
877 |
|
878 def GetSlaveInfos(self, slave_pos): |
|
879 slave = self.GetSlave(slave_pos) |
|
880 if slave is not None: |
|
881 type_infos = slave.getType() |
|
882 device, alignment = self.GetModuleInfos(type_infos) |
|
883 if device is not None: |
|
884 infos = type_infos.copy() |
|
885 infos.update({"physics": device.getPhysics(), |
|
886 "sync_managers": device.GetSyncManagers(), |
|
887 "entries": self.GetSlaveVariables(device)}) |
|
888 return infos |
|
889 return None |
|
890 |
|
891 def GetSlaveVariables(self, slave_pos=None, limits=None, device=None): |
|
892 if device is None and slave_pos is not None: |
|
893 slave = self.GetSlave(slave_pos) |
|
894 if slave is not None: |
|
895 type_infos = slave.getType() |
|
896 device, alignment = self.GetModuleInfos(type_infos) |
|
897 if device is not None: |
|
898 entries = device.GetEntriesList(limits) |
|
899 entries_list = entries.items() |
|
900 entries_list.sort() |
|
901 entries = [] |
|
902 current_index = None |
|
903 current_entry = None |
|
904 for (index, subindex), entry in entries_list: |
|
905 entry["children"] = [] |
|
906 if slave_pos is not None: |
|
907 entry["Position"] = str(slave_pos) |
|
908 entry |
|
909 if index != current_index: |
|
910 current_index = index |
|
911 current_entry = entry |
|
912 entries.append(entry) |
|
913 elif current_entry is not None: |
|
914 current_entry["children"].append(entry) |
|
915 else: |
|
916 entries.append(entry) |
|
917 return entries |
|
918 return [] |
|
919 |
|
920 def GetSlaveVariableDataType(self, slave_pos, index, subindex): |
|
921 slave = self.GetSlave(slave_pos) |
|
922 if slave is not None: |
|
923 device, alignment = self.GetModuleInfos(slave.getType()) |
|
924 if device is not None: |
|
925 entries = device.GetEntriesList() |
|
926 entry_infos = entries.get((index, subindex)) |
|
927 if entry_infos is not None: |
|
928 return entry_infos["Type"] |
|
929 return None |
|
930 |
|
931 def GetNodesVariables(self, vendor=None, slave_pos=None, slave_profile=None, limits=None): |
|
932 entries = [] |
|
933 for slave_position in self.GetSlaves(): |
|
934 if slave_pos is not None and slave_position != slave_pos: |
|
935 continue |
|
936 slave = self.GetSlave(slave_position) |
|
937 type_infos = slave.getType() |
|
938 if vendor is not None and ExtractHexDecValue(type_infos["vendor"]) != vendor: |
|
939 continue |
|
940 device, alignment = self.GetModuleInfos(type_infos) |
|
941 if slave_profile is not None and slave_profile not in device.GetProfileNumbers(): |
|
942 continue |
|
943 entries.extend(self.GetSlaveVariables(slave_position, limits, device)) |
|
944 return entries |
|
945 |
|
946 def GetModuleInfos(self, type_infos): |
|
947 return self.CTNParent.GetModuleInfos(type_infos) |
|
948 |
|
949 def GetSlaveTypesLibrary(self, profile_filter=None): |
|
950 return self.CTNParent.GetModulesLibrary(profile_filter) |
|
951 |
|
952 def GetLibraryVendors(self): |
|
953 return self.CTNParent.GetVendors() |
|
954 |
|
955 def GetDeviceLocationTree(self, slave_pos, current_location, device_name): |
|
956 slave = self.GetSlave(slave_pos) |
|
957 vars = [] |
|
958 if slave is not None: |
|
959 type_infos = slave.getType() |
|
960 |
|
961 device, alignment = self.GetModuleInfos(type_infos) |
|
962 if device is not None: |
|
963 sync_managers = [] |
|
964 for sync_manager in device.getSm(): |
|
965 sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte()) |
|
966 sync_manager_direction = sync_manager_control_byte & 0x0c |
|
967 if sync_manager_direction: |
|
968 sync_managers.append(LOCATION_VAR_OUTPUT) |
|
969 else: |
|
970 sync_managers.append(LOCATION_VAR_INPUT) |
|
971 |
|
972 entries = device.GetEntriesList().items() |
|
973 entries.sort() |
|
974 for (index, subindex), entry in entries: |
|
975 var_size = self.GetSizeOfType(entry["Type"]) |
|
976 if var_size is not None: |
|
977 var_class = VARCLASSCONVERSION.get(entry["PDOMapping"], None) |
|
978 if var_class is not None: |
|
979 if var_class == LOCATION_VAR_INPUT: |
|
980 var_dir = "%I" |
|
981 else: |
|
982 var_dir = "%Q" |
|
983 |
|
984 vars.append({"name": "0x%4.4x-0x%2.2x: %s" % (index, subindex, entry["Name"]), |
|
985 "type": var_class, |
|
986 "size": var_size, |
|
987 "IEC_type": entry["Type"], |
|
988 "var_name": "%s_%4.4x_%2.2x" % ("_".join(device_name.split()), index, subindex), |
|
989 "location": "%s%s%s"%(var_dir, var_size, ".".join(map(str, current_location + |
|
990 (index, subindex)))), |
|
991 "description": "", |
|
992 "children": []}) |
|
993 |
|
994 return vars |
|
995 |
|
996 def CTNTestModified(self): |
|
997 return self.ChangesToSave or not self.ModelIsSaved() |
|
998 |
|
999 def OnCTNSave(self): |
|
1000 config_filepath = self.ConfigFileName() |
|
1001 |
|
1002 config_text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" |
|
1003 config_extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", |
|
1004 "xsi:noNamespaceSchemaLocation" : "EtherCATInfo.xsd"} |
|
1005 config_text += self.Config.generateXMLText("EtherCATConfig", 0, config_extras) |
|
1006 |
|
1007 config_xmlfile = open(config_filepath,"w") |
|
1008 config_xmlfile.write(config_text.encode("utf-8")) |
|
1009 config_xmlfile.close() |
|
1010 |
|
1011 process_filepath = self.ProcessVariablesFileName() |
|
1012 |
|
1013 process_text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" |
|
1014 process_extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance"} |
|
1015 process_text += self.ProcessVariables.generateXMLText("ProcessVariables", 0, process_extras) |
|
1016 |
|
1017 process_xmlfile = open(process_filepath,"w") |
|
1018 process_xmlfile.write(process_text.encode("utf-8")) |
|
1019 process_xmlfile.close() |
|
1020 |
|
1021 self.Buffer.CurrentSaved() |
|
1022 return True |
|
1023 |
|
1024 def GetProcessVariableName(self, location, var_type): |
|
1025 return "__M%s_%s" % (self.GetSizeOfType(var_type), "_".join(map(str, location))) |
|
1026 |
|
1027 def _Generate_C(self, buildpath, locations): |
|
1028 current_location = self.GetCurrentLocation() |
|
1029 # define a unique name for the generated C file |
|
1030 location_str = "_".join(map(lambda x:str(x), current_location)) |
|
1031 |
|
1032 Gen_Ethercatfile_path = os.path.join(buildpath, "ethercat_%s.c"%location_str) |
|
1033 |
|
1034 self.FileGenerator = _EthercatCFileGenerator(self) |
|
1035 |
|
1036 LocationCFilesAndCFLAGS, LDFLAGS, extra_files = ConfigTreeNode._Generate_C(self, buildpath, locations) |
|
1037 |
|
1038 for idx, variable in enumerate(self.ProcessVariables.getvariable()): |
|
1039 name = None |
|
1040 var_type = None |
|
1041 read_from = variable.getReadFrom() |
|
1042 write_to = variable.getWriteTo() |
|
1043 if read_from is not None: |
|
1044 pos = read_from.getPosition() |
|
1045 index = read_from.getIndex() |
|
1046 subindex = read_from.getSubIndex() |
|
1047 location = current_location + (idx, ) |
|
1048 var_type = self.GetSlaveVariableDataType(pos, index, subindex) |
|
1049 name = self.FileGenerator.DeclareVariable( |
|
1050 pos, index, subindex, var_type, "I", |
|
1051 self.GetProcessVariableName(location, var_type)) |
|
1052 if write_to is not None: |
|
1053 pos = write_to.getPosition() |
|
1054 index = write_to.getIndex() |
|
1055 subindex = write_to.getSubIndex() |
|
1056 if name is None: |
|
1057 location = current_location + (idx, ) |
|
1058 var_type = self.GetSlaveVariableDataType(pos, index, subindex) |
|
1059 name = self.GetProcessVariableName(location, var_type) |
|
1060 self.FileGenerator.DeclareVariable( |
|
1061 pos, index, subindex, var_type, "Q", name, True) |
|
1062 |
|
1063 self.FileGenerator.GenerateCFile(Gen_Ethercatfile_path, location_str, self.BaseParams.getIEC_Channel()) |
|
1064 |
|
1065 LocationCFilesAndCFLAGS.append( |
|
1066 (current_location, |
|
1067 [(Gen_Ethercatfile_path, '"-I%s"'%os.path.abspath(self.GetCTRoot().GetIECLibPath()))], |
|
1068 True)) |
|
1069 LDFLAGS.append("-lethercat -lrtdm") |
|
1070 |
|
1071 return LocationCFilesAndCFLAGS, LDFLAGS, extra_files |
|
1072 |
|
1073 ConfNodeMethods = [ |
|
1074 {"bitmap" : "ScanNetwork", |
|
1075 "name" : _("Scan Network"), |
|
1076 "tooltip" : _("Scan Network"), |
|
1077 "method" : "_ScanNetwork"}, |
|
1078 ] |
|
1079 |
|
1080 def CTNGenerate_C(self, buildpath, locations): |
|
1081 current_location = self.GetCurrentLocation() |
|
1082 |
|
1083 slaves = self.GetSlaves() |
|
1084 for slave_pos in slaves: |
|
1085 slave = self.GetSlave(slave_pos) |
|
1086 if slave is not None: |
|
1087 self.FileGenerator.DeclareSlave(slave_pos, slave) |
|
1088 |
|
1089 for location in locations: |
|
1090 loc = location["LOC"][len(current_location):] |
|
1091 slave_pos = loc[0] |
|
1092 if slave_pos in slaves and len(loc) == 3 and location["DIR"] != "M": |
|
1093 self.FileGenerator.DeclareVariable( |
|
1094 slave_pos, loc[1], loc[2], location["IEC_TYPE"], location["DIR"], location["NAME"]) |
|
1095 |
|
1096 return [],"",False |
|
1097 |
|
1098 #------------------------------------------------------------------------------- |
|
1099 # Current Buffering Management Functions |
|
1100 #------------------------------------------------------------------------------- |
|
1101 |
|
1102 """ |
|
1103 Return a copy of the config |
|
1104 """ |
|
1105 def Copy(self, model): |
|
1106 return cPickle.loads(cPickle.dumps(model)) |
|
1107 |
|
1108 def CreateBuffer(self, saved): |
|
1109 self.Buffer = UndoBuffer(cPickle.dumps((self.Config, self.ProcessVariables)), saved) |
|
1110 |
|
1111 def BufferModel(self): |
|
1112 self.Buffer.Buffering(cPickle.dumps((self.Config, self.ProcessVariables))) |
|
1113 |
|
1114 def ModelIsSaved(self): |
|
1115 if self.Buffer is not None: |
|
1116 return self.Buffer.IsCurrentSaved() |
|
1117 else: |
|
1118 return True |
|
1119 |
|
1120 def LoadPrevious(self): |
|
1121 self.Config, self.ProcessVariables = cPickle.loads(self.Buffer.Previous()) |
|
1122 |
|
1123 def LoadNext(self): |
|
1124 self.Config, self.ProcessVariables = cPickle.loads(self.Buffer.Next()) |
|
1125 |
|
1126 def GetBufferState(self): |
|
1127 first = self.Buffer.IsFirst() |
|
1128 last = self.Buffer.IsLast() |
|
1129 return not first, not last |
|
1130 |
|
1131 |
|
1132 SLAVE_PDOS_CONFIGURATION_DECLARATION = """ |
|
1133 /* Slave %(slave)d, "%(device_type)s" |
|
1134 * Vendor ID: 0x%(vendor).8x |
|
1135 * Product code: 0x%(product_code).8x |
|
1136 * Revision number: 0x%(revision_number).8x |
|
1137 */ |
|
1138 |
|
1139 ec_pdo_entry_info_t slave_%(slave)d_pdo_entries[] = { |
|
1140 %(pdos_entries_infos)s |
|
1141 }; |
|
1142 |
|
1143 ec_pdo_info_t slave_%(slave)d_pdos[] = { |
|
1144 %(pdos_infos)s |
|
1145 }; |
|
1146 |
|
1147 ec_sync_info_t slave_%(slave)d_syncs[] = { |
|
1148 %(pdos_sync_infos)s |
|
1149 {0xff} |
|
1150 }; |
|
1151 """ |
|
1152 |
|
1153 SLAVE_CONFIGURATION_TEMPLATE = """ |
|
1154 if (!(slave%(slave)d = ecrt_master_slave_config(master, %(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x))) { |
|
1155 SLOGF(LOG_CRITICAL, "Failed to get slave %(device_type)s configuration at alias %(alias)d and position %(position)d.\\n"); |
|
1156 return -1; |
|
1157 } |
|
1158 |
|
1159 if (ecrt_slave_config_pdos(slave%(slave)d, EC_END, slave_%(slave)d_syncs)) { |
|
1160 SLOGF(LOG_CRITICAL, "Failed to configure PDOs for slave %(device_type)s at alias %(alias)d and position %(position)d.\\n"); |
|
1161 return -1; |
|
1162 } |
|
1163 """ |
|
1164 |
|
1165 SLAVE_INITIALIZATION_TEMPLATE = """ |
|
1166 { |
|
1167 uint8_t value[%(data_size)d]; |
|
1168 EC_WRITE_%(data_type)s((uint8_t *)value, %(data)s); |
|
1169 if (ecrt_master_sdo_download(master, %(slave)d, 0x%(index).4x, 0x%(subindex).2x, (uint8_t *)value, %(data_size)d, &abort_code)) { |
|
1170 SLOGF(LOG_CRITICAL, "Failed to initialize slave %(device_type)s at alias %(alias)d and position %(position)d.\\nError: %%d\\n", abort_code); |
|
1171 return -1; |
|
1172 } |
|
1173 } |
|
1174 """ |
|
1175 |
|
1176 SLAVE_OUTPUT_PDO_DEFAULT_VALUE = """ |
|
1177 { |
|
1178 uint8_t value[%(data_size)d]; |
|
1179 if (ecrt_master_sdo_upload(master, %(slave)d, 0x%(index).4x, 0x%(subindex).2x, (uint8_t *)value, %(data_size)d, &result_size, &abort_code)) { |
|
1180 SLOGF(LOG_CRITICAL, "Failed to get default value for output PDO in slave %(device_type)s at alias %(alias)d and position %(position)d.\\nError: %%ud\\n", abort_code); |
|
1181 return -1; |
|
1182 } |
|
1183 %(real_var)s = EC_READ_%(data_type)s((uint8_t *)value); |
|
1184 } |
|
1185 """ |
|
1186 |
|
1187 def ConfigureVariable(entry_infos, str_completion): |
|
1188 entry_infos["data_type"] = DATATYPECONVERSION.get(entry_infos["var_type"], None) |
|
1189 if entry_infos["data_type"] is None: |
|
1190 raise ValueError, _("Type of location \"%s\" not yet supported!") % entry_infos["var_name"] |
|
1191 |
|
1192 if not entry_infos.get("no_decl", False): |
|
1193 if entry_infos.has_key("real_var"): |
|
1194 str_completion["located_variables_declaration"].append( |
|
1195 "IEC_%(var_type)s %(real_var)s;" % entry_infos) |
|
1196 else: |
|
1197 entry_infos["real_var"] = "beremiz" + entry_infos["var_name"] |
|
1198 str_completion["located_variables_declaration"].extend( |
|
1199 ["IEC_%(var_type)s %(real_var)s;" % entry_infos, |
|
1200 "IEC_%(var_type)s *%(var_name)s = &%(real_var)s;" % entry_infos]) |
|
1201 for declaration in entry_infos.get("extra_declarations", []): |
|
1202 entry_infos["extra_decl"] = declaration |
|
1203 str_completion["located_variables_declaration"].append( |
|
1204 "IEC_%(var_type)s *%(extra_decl)s = &%(real_var)s;" % entry_infos) |
|
1205 elif not entry_infos.has_key("real_var"): |
|
1206 entry_infos["real_var"] = "beremiz" + entry_infos["var_name"] |
|
1207 |
|
1208 str_completion["used_pdo_entry_offset_variables_declaration"].append( |
|
1209 "unsigned int slave%(slave)d_%(index).4x_%(subindex).2x;" % entry_infos) |
|
1210 |
|
1211 if entry_infos["data_type"] == "BIT": |
|
1212 str_completion["used_pdo_entry_offset_variables_declaration"].append( |
|
1213 "unsigned int slave%(slave)d_%(index).4x_%(subindex).2x_bit;" % entry_infos) |
|
1214 |
|
1215 str_completion["used_pdo_entry_configuration"].append( |
|
1216 (" {%(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x, " + |
|
1217 "0x%(index).4x, %(subindex)d, &slave%(slave)d_%(index).4x_%(subindex).2x, " + |
|
1218 "&slave%(slave)d_%(index).4x_%(subindex).2x_bit},") % entry_infos) |
|
1219 |
|
1220 if entry_infos["dir"] == "I": |
|
1221 str_completion["retrieve_variables"].append( |
|
1222 (" %(real_var)s = EC_READ_BIT(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, " + |
|
1223 "slave%(slave)d_%(index).4x_%(subindex).2x_bit);") % entry_infos) |
|
1224 elif entry_infos["dir"] == "Q": |
|
1225 str_completion["publish_variables"].append( |
|
1226 (" EC_WRITE_BIT(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, " + |
|
1227 "slave%(slave)d_%(index).4x_%(subindex).2x_bit, %(real_var)s);") % entry_infos) |
|
1228 |
|
1229 else: |
|
1230 str_completion["used_pdo_entry_configuration"].append( |
|
1231 (" {%(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x, 0x%(index).4x, " + |
|
1232 "%(subindex)d, &slave%(slave)d_%(index).4x_%(subindex).2x},") % entry_infos) |
|
1233 |
|
1234 if entry_infos["dir"] == "I": |
|
1235 str_completion["retrieve_variables"].append( |
|
1236 (" %(real_var)s = EC_READ_%(data_type)s(domain1_pd + " + |
|
1237 "slave%(slave)d_%(index).4x_%(subindex).2x);") % entry_infos) |
|
1238 elif entry_infos["dir"] == "Q": |
|
1239 str_completion["publish_variables"].append( |
|
1240 (" EC_WRITE_%(data_type)s(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, " + |
|
1241 "%(real_var)s);") % entry_infos) |
|
1242 |
|
1243 def ExclusionSortFunction(x, y): |
|
1244 if x["matching"] == y["matching"]: |
|
1245 if x["assigned"] and not y["assigned"]: |
|
1246 return -1 |
|
1247 elif not x["assigned"] and y["assigned"]: |
|
1248 return 1 |
|
1249 return cmp(x["count"], y["count"]) |
|
1250 return -cmp(x["matching"], y["matching"]) |
|
1251 |
|
1252 class _EthercatCFileGenerator: |
|
1253 |
|
1254 def __init__(self, controler): |
|
1255 self.Controler = controler |
|
1256 |
|
1257 self.Slaves = [] |
|
1258 self.UsedVariables = {} |
|
1259 |
|
1260 def __del__(self): |
|
1261 self.Controler = None |
|
1262 |
|
1263 def DeclareSlave(self, slave_index, slave): |
|
1264 self.Slaves.append((slave_index, slave.getInfo().getAutoIncAddr(), slave)) |
|
1265 |
|
1266 def DeclareVariable(self, slave_index, index, subindex, iec_type, dir, name, no_decl=False): |
|
1267 slave_variables = self.UsedVariables.setdefault(slave_index, {}) |
|
1268 |
|
1269 entry_infos = slave_variables.get((index, subindex), None) |
|
1270 if entry_infos is None: |
|
1271 slave_variables[(index, subindex)] = { |
|
1272 "infos": (iec_type, dir, name, no_decl, []), |
|
1273 "mapped": False} |
|
1274 return name |
|
1275 elif entry_infos["infos"][:2] == (iec_type, dir): |
|
1276 if name != entry_infos["infos"][2]: |
|
1277 if dir == "I": |
|
1278 entry_infos["infos"][3].append(name) |
|
1279 return entry_infos["infos"][2] |
|
1280 else: |
|
1281 raise ValueError, _("Output variables can't be defined with different locations (%s and %s)") % (entry_infos["infos"][2], name) |
|
1282 else: |
|
1283 raise ValueError, _("Definition conflict for location \"%s\"") % name |
|
1284 |
|
1285 def GenerateCFile(self, filepath, location_str, master_number): |
|
1286 |
|
1287 # Extract etherlab master code template |
|
1288 plc_etherlab_filepath = os.path.join(os.path.split(__file__)[0], "plc_etherlab.c") |
|
1289 plc_etherlab_file = open(plc_etherlab_filepath, 'r') |
|
1290 plc_etherlab_code = plc_etherlab_file.read() |
|
1291 plc_etherlab_file.close() |
|
1292 |
|
1293 # Initialize strings for formatting master code template |
|
1294 str_completion = { |
|
1295 "location": location_str, |
|
1296 "master_number": master_number, |
|
1297 "located_variables_declaration": [], |
|
1298 "used_pdo_entry_offset_variables_declaration": [], |
|
1299 "used_pdo_entry_configuration": [], |
|
1300 "pdos_configuration_declaration": "", |
|
1301 "slaves_declaration": "", |
|
1302 "slaves_configuration": "", |
|
1303 "slaves_output_pdos_default_values_extraction": "", |
|
1304 "slaves_initialization": "", |
|
1305 "retrieve_variables": [], |
|
1306 "publish_variables": [], |
|
1307 } |
|
1308 |
|
1309 # Initialize variable storing variable mapping state |
|
1310 for slave_entries in self.UsedVariables.itervalues(): |
|
1311 for entry_infos in slave_entries.itervalues(): |
|
1312 entry_infos["mapped"] = False |
|
1313 |
|
1314 # Sort slaves by position (IEC_Channel) |
|
1315 self.Slaves.sort() |
|
1316 # Initialize dictionary storing alias auto-increment position values |
|
1317 alias = {} |
|
1318 |
|
1319 # Generating code for each slave |
|
1320 for (slave_idx, slave_alias, slave) in self.Slaves: |
|
1321 type_infos = slave.getType() |
|
1322 |
|
1323 # Defining slave alias and auto-increment position |
|
1324 if alias.get(slave_alias) is not None: |
|
1325 alias[slave_alias] += 1 |
|
1326 else: |
|
1327 alias[slave_alias] = 0 |
|
1328 slave_pos = (slave_alias, alias[slave_alias]) |
|
1329 |
|
1330 # Extract slave device informations |
|
1331 device, alignment = self.Controler.GetModuleInfos(type_infos) |
|
1332 if device is not None: |
|
1333 |
|
1334 # Extract slaves variables to be mapped |
|
1335 slave_variables = self.UsedVariables.get(slave_idx, {}) |
|
1336 |
|
1337 # Extract slave device object dictionary entries |
|
1338 device_entries = device.GetEntriesList() |
|
1339 |
|
1340 # Adding code for declaring slave in master code template strings |
|
1341 for element in ["vendor", "product_code", "revision_number"]: |
|
1342 type_infos[element] = ExtractHexDecValue(type_infos[element]) |
|
1343 type_infos.update(dict(zip(["slave", "alias", "position"], (slave_idx,) + slave_pos))) |
|
1344 |
|
1345 # Extract slave device CoE informations |
|
1346 device_coe = device.getCoE() |
|
1347 if device_coe is not None: |
|
1348 |
|
1349 # If device support CanOpen over Ethernet, adding code for calling |
|
1350 # init commands when initializing slave in master code template strings |
|
1351 initCmds = [] |
|
1352 for initCmd in device_coe.getInitCmd(): |
|
1353 initCmds.append({ |
|
1354 "Index": ExtractHexDecValue(initCmd.getIndex()), |
|
1355 "Subindex": ExtractHexDecValue(initCmd.getSubIndex()), |
|
1356 "Value": initCmd.getData().getcontent()}) |
|
1357 initCmds.extend(slave.getStartupCommands()) |
|
1358 for initCmd in initCmds: |
|
1359 index = initCmd["Index"] |
|
1360 subindex = initCmd["Subindex"] |
|
1361 entry = device_entries.get((index, subindex), None) |
|
1362 if entry is not None: |
|
1363 data_size = entry["BitSize"] / 8 |
|
1364 data_str = ("0x%%.%dx" % (data_size * 2)) % initCmd["Value"] |
|
1365 init_cmd_infos = { |
|
1366 "index": index, |
|
1367 "subindex": subindex, |
|
1368 "data": data_str, |
|
1369 "data_type": DATATYPECONVERSION.get(entry["Type"]), |
|
1370 "data_size": data_size |
|
1371 } |
|
1372 init_cmd_infos.update(type_infos) |
|
1373 str_completion["slaves_initialization"] += SLAVE_INITIALIZATION_TEMPLATE % init_cmd_infos |
|
1374 |
|
1375 # Extract slave device PDO configuration capabilities |
|
1376 PdoAssign = device_coe.getPdoAssign() |
|
1377 PdoConfig = device_coe.getPdoConfig() |
|
1378 else: |
|
1379 PdoAssign = PdoConfig = False |
|
1380 |
|
1381 # Test if slave has a configuration or need one |
|
1382 if len(device.getTxPdo() + device.getRxPdo()) > 0 or len(slave_variables) > 0 and PdoConfig and PdoAssign: |
|
1383 |
|
1384 str_completion["slaves_declaration"] += "static ec_slave_config_t *slave%(slave)d = NULL;\n" % type_infos |
|
1385 str_completion["slaves_configuration"] += SLAVE_CONFIGURATION_TEMPLATE % type_infos |
|
1386 |
|
1387 # Initializing |
|
1388 pdos_infos = { |
|
1389 "pdos_entries_infos": [], |
|
1390 "pdos_infos": [], |
|
1391 "pdos_sync_infos": [], |
|
1392 } |
|
1393 pdos_infos.update(type_infos) |
|
1394 |
|
1395 sync_managers = [] |
|
1396 for sync_manager_idx, sync_manager in enumerate(device.getSm()): |
|
1397 sync_manager_infos = { |
|
1398 "index": sync_manager_idx, |
|
1399 "name": sync_manager.getcontent(), |
|
1400 "slave": slave_idx, |
|
1401 "pdos": [], |
|
1402 "pdos_number": 0, |
|
1403 } |
|
1404 |
|
1405 sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte()) |
|
1406 sync_manager_direction = sync_manager_control_byte & 0x0c |
|
1407 sync_manager_watchdog = sync_manager_control_byte & 0x40 |
|
1408 if sync_manager_direction: |
|
1409 sync_manager_infos["sync_manager_type"] = "EC_DIR_OUTPUT" |
|
1410 else: |
|
1411 sync_manager_infos["sync_manager_type"] = "EC_DIR_INPUT" |
|
1412 if sync_manager_watchdog: |
|
1413 sync_manager_infos["watchdog"] = "EC_WD_ENABLE" |
|
1414 else: |
|
1415 sync_manager_infos["watchdog"] = "EC_WD_DISABLE" |
|
1416 |
|
1417 sync_managers.append(sync_manager_infos) |
|
1418 |
|
1419 pdos_index = [] |
|
1420 exclusive_pdos = {} |
|
1421 selected_pdos = [] |
|
1422 for pdo, pdo_type in ([(pdo, "Inputs") for pdo in device.getTxPdo()] + |
|
1423 [(pdo, "Outputs") for pdo in device.getRxPdo()]): |
|
1424 |
|
1425 pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent()) |
|
1426 pdos_index.append(pdo_index) |
|
1427 |
|
1428 excluded_list = pdo.getExclude() |
|
1429 if len(excluded_list) > 0: |
|
1430 exclusion_list = [pdo_index] |
|
1431 for excluded in excluded_list: |
|
1432 exclusion_list.append(ExtractHexDecValue(excluded.getcontent())) |
|
1433 exclusion_list.sort() |
|
1434 |
|
1435 exclusion_scope = exclusive_pdos.setdefault(tuple(exclusion_list), []) |
|
1436 |
|
1437 entries = pdo.getEntry() |
|
1438 pdo_mapping_match = { |
|
1439 "index": pdo_index, |
|
1440 "matching": 0, |
|
1441 "count": len(entries), |
|
1442 "assigned": pdo.getSm() is not None |
|
1443 } |
|
1444 exclusion_scope.append(pdo_mapping_match) |
|
1445 |
|
1446 for entry in entries: |
|
1447 index = ExtractHexDecValue(entry.getIndex().getcontent()) |
|
1448 subindex = ExtractHexDecValue(entry.getSubIndex()) |
|
1449 if slave_variables.get((index, subindex), None) is not None: |
|
1450 pdo_mapping_match["matching"] += 1 |
|
1451 |
|
1452 elif pdo.getMandatory(): |
|
1453 selected_pdos.append(pdo_index) |
|
1454 |
|
1455 excluded_pdos = [] |
|
1456 for exclusion_scope in exclusive_pdos.itervalues(): |
|
1457 exclusion_scope.sort(ExclusionSortFunction) |
|
1458 start_excluding_index = 0 |
|
1459 if exclusion_scope[0]["matching"] > 0: |
|
1460 selected_pdos.append(exclusion_scope[0]["index"]) |
|
1461 start_excluding_index = 1 |
|
1462 excluded_pdos.extend([pdo["index"] for pdo in exclusion_scope[start_excluding_index:] if PdoAssign or not pdo["assigned"]]) |
|
1463 |
|
1464 for pdo, pdo_type in ([(pdo, "Inputs") for pdo in device.getTxPdo()] + |
|
1465 [(pdo, "Outputs") for pdo in device.getRxPdo()]): |
|
1466 entries = pdo.getEntry() |
|
1467 |
|
1468 pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent()) |
|
1469 if pdo_index in excluded_pdos: |
|
1470 continue |
|
1471 |
|
1472 pdo_needed = pdo_index in selected_pdos |
|
1473 |
|
1474 entries_infos = [] |
|
1475 |
|
1476 for entry in entries: |
|
1477 index = ExtractHexDecValue(entry.getIndex().getcontent()) |
|
1478 subindex = ExtractHexDecValue(entry.getSubIndex()) |
|
1479 entry_infos = { |
|
1480 "index": index, |
|
1481 "subindex": subindex, |
|
1482 "name": ExtractName(entry.getName()), |
|
1483 "bitlen": entry.getBitLen(), |
|
1484 } |
|
1485 entry_infos.update(type_infos) |
|
1486 entries_infos.append(" {0x%(index).4x, 0x%(subindex).2x, %(bitlen)d}, /* %(name)s */" % entry_infos) |
|
1487 |
|
1488 entry_declaration = slave_variables.get((index, subindex), None) |
|
1489 if entry_declaration is not None and not entry_declaration["mapped"]: |
|
1490 pdo_needed = True |
|
1491 |
|
1492 entry_infos.update(dict(zip(["var_type", "dir", "var_name", "no_decl", "extra_declarations"], |
|
1493 entry_declaration["infos"]))) |
|
1494 entry_declaration["mapped"] = True |
|
1495 |
|
1496 entry_type = entry.getDataType().getcontent() |
|
1497 if entry_infos["var_type"] != entry_type: |
|
1498 message = _("Wrong type for location \"%s\"!") % entry_infos["var_name"] |
|
1499 if (self.Controler.GetSizeOfType(entry_infos["var_type"]) != |
|
1500 self.Controler.GetSizeOfType(entry_type)): |
|
1501 raise ValueError, message |
|
1502 else: |
|
1503 self.Controler.GetCTRoot().logger.write_warning(_("Warning: ") + message + "\n") |
|
1504 |
|
1505 if (entry_infos["dir"] == "I" and pdo_type != "Inputs" or |
|
1506 entry_infos["dir"] == "Q" and pdo_type != "Outputs"): |
|
1507 raise ValueError, _("Wrong direction for location \"%s\"!") % entry_infos["var_name"] |
|
1508 |
|
1509 ConfigureVariable(entry_infos, str_completion) |
|
1510 |
|
1511 elif pdo_type == "Outputs" and entry.getDataType() is not None and device_coe is not None: |
|
1512 data_type = entry.getDataType().getcontent() |
|
1513 entry_infos["dir"] = "Q" |
|
1514 entry_infos["data_size"] = max(1, entry_infos["bitlen"] / 8) |
|
1515 entry_infos["data_type"] = DATATYPECONVERSION.get(data_type) |
|
1516 entry_infos["var_type"] = data_type |
|
1517 entry_infos["real_var"] = "slave%(slave)d_%(index).4x_%(subindex).2x_default" % entry_infos |
|
1518 |
|
1519 ConfigureVariable(entry_infos, str_completion) |
|
1520 |
|
1521 str_completion["slaves_output_pdos_default_values_extraction"] += \ |
|
1522 SLAVE_OUTPUT_PDO_DEFAULT_VALUE % entry_infos |
|
1523 |
|
1524 if pdo_needed: |
|
1525 for excluded in pdo.getExclude(): |
|
1526 excluded_index = ExtractHexDecValue(excluded.getcontent()) |
|
1527 if excluded_index not in excluded_pdos: |
|
1528 excluded_pdos.append(excluded_index) |
|
1529 |
|
1530 sm = pdo.getSm() |
|
1531 if sm is None: |
|
1532 for sm_idx, sync_manager in enumerate(sync_managers): |
|
1533 if sync_manager["name"] == pdo_type: |
|
1534 sm = sm_idx |
|
1535 if sm is None: |
|
1536 raise ValueError, _("No sync manager available for %s pdo!") % pdo_type |
|
1537 |
|
1538 sync_managers[sm]["pdos_number"] += 1 |
|
1539 sync_managers[sm]["pdos"].append( |
|
1540 {"slave": slave_idx, |
|
1541 "index": pdo_index, |
|
1542 "name": ExtractName(pdo.getName()), |
|
1543 "type": pdo_type, |
|
1544 "entries": entries_infos, |
|
1545 "entries_number": len(entries_infos), |
|
1546 "fixed": pdo.getFixed() == True}) |
|
1547 |
|
1548 if PdoConfig and PdoAssign: |
|
1549 dynamic_pdos = {} |
|
1550 dynamic_pdos_number = 0 |
|
1551 for category, min_index, max_index in [("Inputs", 0x1600, 0x1800), |
|
1552 ("Outputs", 0x1a00, 0x1C00)]: |
|
1553 for sync_manager in sync_managers: |
|
1554 if sync_manager["name"] == category: |
|
1555 category_infos = dynamic_pdos.setdefault(category, {}) |
|
1556 category_infos["sync_manager"] = sync_manager |
|
1557 category_infos["pdos"] = [pdo for pdo in category_infos["sync_manager"]["pdos"] |
|
1558 if not pdo["fixed"] and pdo["type"] == category] |
|
1559 category_infos["current_index"] = min_index |
|
1560 category_infos["max_index"] = max_index |
|
1561 break |
|
1562 |
|
1563 for (index, subindex), entry_declaration in slave_variables.iteritems(): |
|
1564 |
|
1565 if not entry_declaration["mapped"]: |
|
1566 entry = device_entries.get((index, subindex), None) |
|
1567 if entry is None: |
|
1568 raise ValueError, _("Unknown entry index 0x%4.4x, subindex 0x%2.2x for device %s") % \ |
|
1569 (index, subindex, type_infos["device_type"]) |
|
1570 |
|
1571 entry_infos = { |
|
1572 "index": index, |
|
1573 "subindex": subindex, |
|
1574 "name": entry["Name"], |
|
1575 "bitlen": entry["BitSize"], |
|
1576 } |
|
1577 entry_infos.update(type_infos) |
|
1578 |
|
1579 entry_infos.update(dict(zip(["var_type", "dir", "var_name", "no_decl", "extra_declarations"], |
|
1580 entry_declaration["infos"]))) |
|
1581 entry_declaration["mapped"] = True |
|
1582 |
|
1583 if entry_infos["var_type"] != entry["Type"]: |
|
1584 message = _("Wrong type for location \"%s\"!") % entry_infos["var_name"] |
|
1585 if (self.Controler.GetSizeOfType(entry_infos["var_type"]) != |
|
1586 self.Controler.GetSizeOfType(entry["Type"])): |
|
1587 raise ValueError, message |
|
1588 else: |
|
1589 self.Controler.GetCTRoot().logger.write_warning(message + "\n") |
|
1590 |
|
1591 if entry_infos["dir"] == "I" and entry["PDOMapping"] in ["T", "RT"]: |
|
1592 pdo_type = "Inputs" |
|
1593 elif entry_infos["dir"] == "Q" and entry["PDOMapping"] in ["R", "RT"]: |
|
1594 pdo_type = "Outputs" |
|
1595 else: |
|
1596 raise ValueError, _("Wrong direction for location \"%s\"!") % entry_infos["var_name"] |
|
1597 |
|
1598 if not dynamic_pdos.has_key(pdo_type): |
|
1599 raise ValueError, _("No Sync manager defined for %s!") % pdo_type |
|
1600 |
|
1601 ConfigureVariable(entry_infos, str_completion) |
|
1602 |
|
1603 if len(dynamic_pdos[pdo_type]["pdos"]) > 0: |
|
1604 pdo = dynamic_pdos[pdo_type]["pdos"][0] |
|
1605 else: |
|
1606 while dynamic_pdos[pdo_type]["current_index"] in pdos_index: |
|
1607 dynamic_pdos[pdo_type]["current_index"] += 1 |
|
1608 if dynamic_pdos[pdo_type]["current_index"] >= dynamic_pdos[pdo_type]["max_index"]: |
|
1609 raise ValueError, _("No more free PDO index available for %s!") % pdo_type |
|
1610 pdos_index.append(dynamic_pdos[pdo_type]["current_index"]) |
|
1611 |
|
1612 dynamic_pdos_number += 1 |
|
1613 pdo = {"slave": slave_idx, |
|
1614 "index": dynamic_pdos[pdo_type]["current_index"], |
|
1615 "name": "Dynamic PDO %d" % dynamic_pdos_number, |
|
1616 "type": pdo_type, |
|
1617 "entries": [], |
|
1618 "entries_number": 0, |
|
1619 "fixed": False} |
|
1620 dynamic_pdos[pdo_type]["sync_manager"]["pdos_number"] += 1 |
|
1621 dynamic_pdos[pdo_type]["sync_manager"]["pdos"].append(pdo) |
|
1622 dynamic_pdos[pdo_type]["pdos"].append(pdo) |
|
1623 |
|
1624 pdo["entries"].append(" {0x%(index).4x, 0x%(subindex).2x, %(bitlen)d}, /* %(name)s */" % entry_infos) |
|
1625 if entry_infos["bitlen"] < alignment: |
|
1626 print (alignment, entry_infos["bitlen"]) |
|
1627 pdo["entries"].append(" {0x0000, 0x00, %d}, /* None */" % (alignment - entry_infos["bitlen"])) |
|
1628 pdo["entries_number"] += 1 |
|
1629 |
|
1630 if pdo["entries_number"] == 255: |
|
1631 dynamic_pdos[pdo_type]["pdos"].pop(0) |
|
1632 |
|
1633 pdo_offset = 0 |
|
1634 entry_offset = 0 |
|
1635 for sync_manager_infos in sync_managers: |
|
1636 |
|
1637 for pdo_infos in sync_manager_infos["pdos"]: |
|
1638 pdo_infos["offset"] = entry_offset |
|
1639 pdo_entries = pdo_infos["entries"] |
|
1640 pdos_infos["pdos_infos"].append( |
|
1641 (" {0x%(index).4x, %(entries_number)d, " + |
|
1642 "slave_%(slave)d_pdo_entries + %(offset)d}, /* %(name)s */") % pdo_infos) |
|
1643 entry_offset += len(pdo_entries) |
|
1644 pdos_infos["pdos_entries_infos"].extend(pdo_entries) |
|
1645 |
|
1646 sync_manager_infos["offset"] = pdo_offset |
|
1647 pdo_offset_shift = sync_manager_infos["pdos_number"] |
|
1648 pdos_infos["pdos_sync_infos"].append( |
|
1649 (" {%(index)d, %(sync_manager_type)s, %(pdos_number)d, " + |
|
1650 ("slave_%(slave)d_pdos + %(offset)d" if pdo_offset_shift else "NULL") + |
|
1651 ", %(watchdog)s},") % sync_manager_infos) |
|
1652 pdo_offset += pdo_offset_shift |
|
1653 |
|
1654 for element in ["pdos_entries_infos", "pdos_infos", "pdos_sync_infos"]: |
|
1655 pdos_infos[element] = "\n".join(pdos_infos[element]) |
|
1656 |
|
1657 str_completion["pdos_configuration_declaration"] += SLAVE_PDOS_CONFIGURATION_DECLARATION % pdos_infos |
|
1658 |
|
1659 for (index, subindex), entry_declaration in slave_variables.iteritems(): |
|
1660 if not entry_declaration["mapped"]: |
|
1661 message = _("Entry index 0x%4.4x, subindex 0x%2.2x not mapped for device %s") % \ |
|
1662 (index, subindex, type_infos["device_type"]) |
|
1663 self.Controler.GetCTRoot().logger.write_warning(_("Warning: ") + message + "\n") |
|
1664 |
|
1665 for element in ["used_pdo_entry_offset_variables_declaration", |
|
1666 "used_pdo_entry_configuration", |
|
1667 "located_variables_declaration", |
|
1668 "retrieve_variables", |
|
1669 "publish_variables"]: |
|
1670 str_completion[element] = "\n".join(str_completion[element]) |
|
1671 |
|
1672 etherlabfile = open(filepath, 'w') |
|
1673 etherlabfile.write(plc_etherlab_code % str_completion) |
|
1674 etherlabfile.close() |
|
1675 |
15 |
1676 #-------------------------------------------------- |
16 #-------------------------------------------------- |
1677 # Ethercat ConfNode |
17 # Ethercat ConfNode |
1678 #-------------------------------------------------- |
18 #-------------------------------------------------- |
1679 |
19 |