|
1 #!/usr/bin/env python |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 # This file is part of Beremiz |
|
5 # |
|
6 # Copyright (C) 2013: Real-Time & Embedded Systems (RTES) Lab., University of Seoul |
|
7 # |
|
8 # See COPYING file for copyrights details. |
|
9 |
|
10 import os |
|
11 |
|
12 import wx |
|
13 import wx.grid |
|
14 import wx.gizmos |
|
15 import wx.lib.buttons |
|
16 |
|
17 # -------------------------------------------------------------------- |
|
18 from controls import CustomGrid, CustomTable |
|
19 # -------------------------------------------------------------------- |
|
20 |
|
21 # ------------ for SDO Management -------------------- |
|
22 import string |
|
23 import wx.grid as gridlib |
|
24 #------------------------------------------------------------- |
|
25 |
|
26 # ------------ for register management --------------- |
|
27 from xml.dom import minidom |
|
28 #------------------------------------------------------------- |
|
29 |
|
30 # ----------------------------- For Sync Manager Table ----------------------------------- |
|
31 def GetSyncManagersTableColnames(): |
|
32 """ |
|
33 Returns column names of SyncManager Table in Slave state panel. |
|
34 """ |
|
35 _ = lambda x : x |
|
36 return ["#", _("Name"), _("Start Address"), _("Default Size"), _("Control Byte"), _("Enable")] |
|
37 |
|
38 #------------------------------------------------------------------------------- |
|
39 # Sync Managers Table |
|
40 #------------------------------------------------------------------------------- |
|
41 class SyncManagersTable(CustomTable): |
|
42 def GetValue(self, row, col): |
|
43 if row < self.GetNumberRows(): |
|
44 if col == 0: |
|
45 return row |
|
46 return self.data[row].get(self.GetColLabelValue(col, False), "") |
|
47 |
|
48 #------------------------------------------------------------------------------- |
|
49 # EtherCAT Management Treebook |
|
50 #------------------------------------------------------------------------------- |
|
51 class EtherCATManagementTreebook(wx.Treebook): |
|
52 def __init__(self, parent, controler, node_editor): |
|
53 """ |
|
54 Constructor |
|
55 @param parent: Reference to the parent wx.ScrolledWindow object |
|
56 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
57 @param node_editor: Reference to Beremiz frame |
|
58 """ |
|
59 wx.Treebook.__init__(self, parent, -1, size=wx.DefaultSize, style=wx.BK_DEFAULT) |
|
60 self.parent = parent |
|
61 self.Controler = controler |
|
62 self.NodeEditor = node_editor |
|
63 |
|
64 self.EtherCATManagementClassObject = {} |
|
65 |
|
66 # fill EtherCAT Management Treebook |
|
67 for pname, pclass, subs in [ |
|
68 ("Slave State", SlaveStatePanelClass, []), |
|
69 ("SDO Management", SDOPanelClass, []), |
|
70 ("PDO Monitoring", PDOPanelClass, []), |
|
71 ("ESC Management", EEPROMAccessPanel, [ |
|
72 ("Smart View", SlaveSiiSmartView), |
|
73 ("Hex View", HexView)]), |
|
74 ("Register Access", RegisterAccessPanel, [])]: |
|
75 self.AddPage(pclass(self, self.Controler), pname) |
|
76 for spname, spclass in subs: |
|
77 self.AddSubPage(spclass(self, self.Controler), spname) |
|
78 |
|
79 self.Bind(wx.EVT_TREEBOOK_PAGE_CHANGED, self.OnPageChanged) |
|
80 self.Bind(wx.EVT_TREEBOOK_PAGE_CHANGING, self.OnPageChanging) |
|
81 |
|
82 def OnPageChanged(self, event): |
|
83 old = event.GetOldSelection() |
|
84 new = event.GetSelection() |
|
85 sel = event.GetSelection() |
|
86 event.Skip() |
|
87 |
|
88 def OnPageChanging(self, event): |
|
89 old = event.GetOldSelection() |
|
90 new = event.GetSelection() |
|
91 sel = event.GetSelection() |
|
92 event.Skip() |
|
93 |
|
94 #------------------------------------------------------------------------------- |
|
95 # For SlaveState Panel |
|
96 #------------------------------------------------------------------------------- |
|
97 class SlaveStatePanelClass(wx.Panel): |
|
98 def __init__(self, parent, controler): |
|
99 """ |
|
100 Constructor |
|
101 @param parent: Reference to the parent EtherCATManagementTreebook class |
|
102 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
103 """ |
|
104 wx.Panel.__init__(self, parent, -1, (0, 0), size=wx.DefaultSize, style = wx.SUNKEN_BORDER) |
|
105 self.Controler = controler |
|
106 self.parent = parent |
|
107 |
|
108 # initialize SlaveStatePanel UI dictionaries |
|
109 self.StaticBoxDic = {} |
|
110 self.StaticTextDic = {} |
|
111 self.TextCtrlDic = {} |
|
112 self.ButtonDic = {} |
|
113 |
|
114 # iniitalize BoxSizer and FlexGridSizer |
|
115 self.SizerDic = { |
|
116 "SlaveState_main_sizer" : wx.BoxSizer(wx.VERTICAL), |
|
117 "SlaveState_inner_main_sizer" : wx.FlexGridSizer(cols=1, hgap=50, rows=3, vgap=10), |
|
118 "SlaveInfosDetailsInnerSizer" : wx.FlexGridSizer(cols=4, hgap=10, rows=2, vgap=10), |
|
119 "SyncManagerInnerSizer" : wx.FlexGridSizer(cols=1, hgap=5, rows=1, vgap=5), |
|
120 "SlaveState_sizer" : wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=10), |
|
121 "SlaveState_up_sizer" : wx.FlexGridSizer(cols=4, hgap=10, rows=2, vgap=10), |
|
122 "SlaveState_down_sizer" : wx.FlexGridSizer(cols=2, hgap=10, rows=1, vgap=10)} |
|
123 |
|
124 # initialize StaticBox and StaticBoxSizer |
|
125 for box_name, box_label in [ |
|
126 ("SlaveInfosDetailsBox", "Slave Informations"), |
|
127 ("SyncManagerBox", "Sync Manager"), |
|
128 ("SlaveStateBox", "Slave State Transition && Monitoring")]: |
|
129 self.StaticBoxDic[box_name] = wx.StaticBox(self, label=_(box_label)) |
|
130 self.SizerDic[box_name] = wx.StaticBoxSizer(self.StaticBoxDic[box_name]) |
|
131 |
|
132 for statictext_name, statictext_label, textctrl_name in [ |
|
133 ("VendorLabel", "Vendor:", "vendor"), |
|
134 ("ProductcodeLabel", "Product code:", "product_code"), |
|
135 ("RevisionnumberLabel", "Slave Count:", "revision_number"), |
|
136 ("PhysicsLabel", "Physics:", "physics")]: |
|
137 self.StaticTextDic[statictext_name] = wx.StaticText(self, label=_(statictext_label)) |
|
138 self.TextCtrlDic[textctrl_name] = wx.TextCtrl(self, size=wx.Size(130, 24), style=wx.TE_READONLY) |
|
139 self.SizerDic["SlaveInfosDetailsInnerSizer"].AddMany([self.StaticTextDic[statictext_name], |
|
140 self.TextCtrlDic[textctrl_name]]) |
|
141 |
|
142 self.SizerDic["SlaveInfosDetailsBox"].AddSizer(self.SizerDic["SlaveInfosDetailsInnerSizer"]) |
|
143 |
|
144 self.SyncManagersGrid = CustomGrid(self, size=wx.Size(605,155), style=wx.VSCROLL) |
|
145 |
|
146 self.SizerDic["SyncManagerInnerSizer"].Add(self.SyncManagersGrid) |
|
147 self.SizerDic["SyncManagerBox"].Add(self.SizerDic["SyncManagerInnerSizer"]) |
|
148 |
|
149 for button_name, button_id, button_label, button_tooltipstring, event_method, sub_item in [ |
|
150 ("InitButton", 0, "INIT", "State Transition to \"Init\" State", self.OnButtonClick, []), |
|
151 ("PreOPButton", 1, "PREOP", "State Transition to \"PreOP\" State", self.OnButtonClick, [ |
|
152 ("TargetStateLabel", "Target State:" , "TargetState")]), |
|
153 ("SafeOPButton", 2, "SAFEOP", "State Transition to \"SafeOP\" State", self.OnButtonClick, []), |
|
154 ("OPButton", 3, "OP", "State Transition to \"OP\" State", self.OnButtonClick, [ |
|
155 ("CurrentStateLabel", "Current State:", "CurrentState")])]: |
|
156 self.ButtonDic[button_name] = wx.Button(self, id=button_id ,label=_(button_label)) |
|
157 self.ButtonDic[button_name].Bind(wx.EVT_BUTTON, event_method) |
|
158 self.ButtonDic[button_name].SetToolTipString(button_tooltipstring) |
|
159 self.SizerDic["SlaveState_up_sizer"].Add(self.ButtonDic[button_name]) |
|
160 for statictext_name, statictext_label, textctrl_name in sub_item : |
|
161 self.StaticTextDic[statictext_name] = wx.StaticText(self, label=_(statictext_label)) |
|
162 self.TextCtrlDic[textctrl_name] = wx.TextCtrl(self, size=wx.DefaultSize, style=wx.TE_READONLY) |
|
163 self.SizerDic["SlaveState_up_sizer"].AddMany([self.StaticTextDic[statictext_name], |
|
164 self.TextCtrlDic[textctrl_name]]) |
|
165 |
|
166 for button_name, button_label, button_tooltipstring, event_method in [ |
|
167 ("StartTimerButton", "Start State Monitoring", "Slave State Update Restart", self.StartTimer), |
|
168 ("StopTimerButton", "Stop State Monitoring", "Slave State Update Stop", self.CurrentStateThreadStop)]: |
|
169 self.ButtonDic[button_name] = wx.Button(self, label=_(button_label)) |
|
170 self.ButtonDic[button_name].Bind(wx.EVT_BUTTON, event_method) |
|
171 self.ButtonDic[button_name].SetToolTipString(button_tooltipstring) |
|
172 self.SizerDic["SlaveState_down_sizer"].Add(self.ButtonDic[button_name]) |
|
173 |
|
174 self.SizerDic["SlaveState_sizer"].AddMany([self.SizerDic["SlaveState_up_sizer"], |
|
175 self.SizerDic["SlaveState_down_sizer"]]) |
|
176 |
|
177 self.SizerDic["SlaveStateBox"].Add(self.SizerDic["SlaveState_sizer"]) |
|
178 |
|
179 self.SizerDic["SlaveState_inner_main_sizer"].AddMany([ |
|
180 self.SizerDic["SlaveInfosDetailsBox"], self.SizerDic["SyncManagerBox"], |
|
181 self.SizerDic["SlaveStateBox"]]) |
|
182 |
|
183 self.SizerDic["SlaveState_main_sizer"].Add(self.SizerDic["SlaveState_inner_main_sizer"]) |
|
184 |
|
185 self.SetSizer(self.SizerDic["SlaveState_main_sizer"]) |
|
186 |
|
187 # register a timer for periodic exectuion of slave state update (period: 1000 ms) |
|
188 self.Bind(wx.EVT_TIMER, self.GetCurrentState) |
|
189 |
|
190 self.CreateSyncManagerTable() |
|
191 |
|
192 self.Centre() |
|
193 |
|
194 def CreateSyncManagerTable(self): |
|
195 """ |
|
196 Create grid for "SyncManager" |
|
197 """ |
|
198 # declare Table object |
|
199 self.SyncManagersTable = SyncManagersTable(self, [], GetSyncManagersTableColnames()) |
|
200 self.SyncManagersGrid.SetTable(self.SyncManagersTable) |
|
201 # set grid alignment attr. (CENTER) |
|
202 self.SyncManagersGridColAlignements = [wx.ALIGN_CENTRE, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE, |
|
203 wx.ALIGN_CENTRE, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE] |
|
204 # set grid size |
|
205 self.SyncManagersGridColSizes = [40, 150, 100, 100, 100, 100] |
|
206 self.SyncManagersGrid.SetRowLabelSize(0) |
|
207 for col in range(self.SyncManagersTable.GetNumberCols()): |
|
208 attr = wx.grid.GridCellAttr() |
|
209 attr.SetAlignment(self.SyncManagersGridColAlignements[col], wx.ALIGN_CENTRE) |
|
210 self.SyncManagersGrid.SetColAttr(col, attr) |
|
211 self.SyncManagersGrid.SetColMinimalWidth(col, self.SyncManagersGridColSizes[col]) |
|
212 self.SyncManagersGrid.AutoSizeColumn(col, False) |
|
213 |
|
214 self.RefreshSlaveInfos() |
|
215 |
|
216 def RefreshSlaveInfos(self): |
|
217 """ |
|
218 Fill data in "Slave Information" and "SyncManager" |
|
219 """ |
|
220 slave_infos = self.Controler.GetSlaveInfos() |
|
221 sync_manager_section = ["vendor", "product_code", "revision_number", "physics"] |
|
222 if slave_infos is not None: |
|
223 # this method is same as "TextCtrl.SetValue" |
|
224 for textctrl_name in sync_manager_section: |
|
225 self.TextCtrlDic[textctrl_name].SetValue(slave_infos[textctrl_name]) |
|
226 self.SyncManagersTable.SetData(slave_infos["sync_managers"]) |
|
227 self.SyncManagersTable.ResetView(self.SyncManagersGrid) |
|
228 else: |
|
229 for textctrl_name in sync_manager_section: |
|
230 self.TextCtrlDic[textctrl_name].SetValue("") |
|
231 self.SyncManagersTable.SetData([]) |
|
232 self.SyncManagersTable.ResetView(self.SyncManagersGrid) |
|
233 |
|
234 def OnButtonClick(self, event): |
|
235 """ |
|
236 Event handler for slave state transition button click (Init, PreOP, SafeOP, OP button) |
|
237 @param event : wx.EVT_BUTTON object |
|
238 """ |
|
239 check_connect_flag = self.Controler.CommonMethod.CheckConnect(False) |
|
240 if check_connect_flag : |
|
241 state_dic = ["INIT", "PREOP", "SAFEOP", "OP"] |
|
242 |
|
243 # If target state is one of {INIT, PREOP, SAFEOP}, request slave state transition immediately. |
|
244 if event.GetId() < 3 : |
|
245 self.Controler.CommonMethod.RequestSlaveState(state_dic[event.GetId()]) |
|
246 self.TextCtrlDic["TargetState"].SetValue(state_dic[event.GetId()]) |
|
247 |
|
248 # If target state is OP, first check "PLC status". |
|
249 # (1) If current PLC status is "Started", then request slave state transition |
|
250 # (2) Otherwise, show error message and return |
|
251 else : |
|
252 status, count = self.Controler.GetCTRoot()._connector.GetPLCstatus() |
|
253 if status == "Started" : |
|
254 self.Controler.CommonMethod.RequestSlaveState("OP") |
|
255 self.TextCtrlDic["TargetState"].SetValue("OP") |
|
256 else : |
|
257 self.Controler.CommonMethod.CreateErrorDialog("PLC is Not Started") |
|
258 |
|
259 def GetCurrentState(self, event): |
|
260 """ |
|
261 Timer event handler for periodic slave state monitoring (Default period: 1 sec = 1000 msec). |
|
262 @param event : wx.TIMER object |
|
263 """ |
|
264 check_connect_flag = self.Controler.CommonMethod.CheckConnect(True) |
|
265 if check_connect_flag: |
|
266 returnVal = self.Controler.CommonMethod.GetSlaveStateFromSlave() |
|
267 line = returnVal.split("\n") |
|
268 try : |
|
269 self.SetCurrentState(line[self.Controler.GetSlavePos()]) |
|
270 except : |
|
271 pass |
|
272 |
|
273 def SetCurrentState(self, line): |
|
274 """ |
|
275 Show current slave state using the executiob result of "ethercat slaves" command. |
|
276 @param line : result of "ethercat slaves" command |
|
277 """ |
|
278 state_array = ["INIT", "PREOP", "SAFEOP", "OP"] |
|
279 try : |
|
280 # parse the execution result of "ethercat slaves" command |
|
281 # Result example : 0 0:0 PREOP + EL9800 (V4.30) (PIC24, SPI, ET1100) |
|
282 token = line.split(" ") |
|
283 if token[2] in state_array: |
|
284 self.TextCtrlDic["CurrentState"].SetValue(token[2]) |
|
285 except : |
|
286 pass |
|
287 |
|
288 def StartTimer(self, event): |
|
289 """ |
|
290 Event handler for "Start State Monitoring" button. |
|
291 - start slave state monitoring thread |
|
292 @param event : wx.EVT_BUTTON object |
|
293 """ |
|
294 self.SlaveStateThread = wx.Timer(self) |
|
295 # set timer period (1000 ms) |
|
296 self.SlaveStateThread.Start(1000) |
|
297 |
|
298 def CurrentStateThreadStop(self, event): |
|
299 """ |
|
300 Event handler for "Stop State Monitoring" button. |
|
301 - stop slave state monitoring thread |
|
302 @param event : wx.EVT_BUTTON object |
|
303 """ |
|
304 try: |
|
305 self.SlaveStateThread.Stop() |
|
306 except: |
|
307 pass |
|
308 |
|
309 #------------------------------------------------------------------------------- |
|
310 # For SDO Management Panel |
|
311 #------------------------------------------------------------------------------- |
|
312 class SDOPanelClass(wx.Panel): |
|
313 def __init__(self, parent, controler): |
|
314 """ |
|
315 Constructor |
|
316 @param parent: Reference to the parent EtherCATManagementTreebook class |
|
317 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
318 """ |
|
319 wx.Panel.__init__(self, parent, -1) |
|
320 |
|
321 self.DatatypeDescription, self.CommunicationObject, self.ManufacturerSpecific, \ |
|
322 self.ProfileSpecific, self.Reserved, self.AllSDOData = range(6) |
|
323 |
|
324 self.Controler = controler |
|
325 |
|
326 self.SDOManagementMainSizer = wx.BoxSizer(wx.VERTICAL) |
|
327 self.SDOManagementInnerMainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=10) |
|
328 |
|
329 self.SDOUpdate = wx.Button(self, label=_('update')) |
|
330 self.SDOUpdate.Bind(wx.EVT_BUTTON, self.SDOInfoUpdate) |
|
331 |
|
332 self.CallSDONoteBook = SDONoteBook(self, controler=self.Controler) |
|
333 self.SDOManagementInnerMainSizer.Add(self.SDOUpdate) |
|
334 self.SDOManagementInnerMainSizer.Add(self.CallSDONoteBook, wx.ALL | wx.EXPAND) |
|
335 |
|
336 self.SDOManagementMainSizer.Add(self.SDOManagementInnerMainSizer) |
|
337 |
|
338 self.SetSizer(self.SDOManagementMainSizer) |
|
339 |
|
340 def SDOInfoUpdate(self, event): |
|
341 """ |
|
342 Evenet handler for SDO "update" button. |
|
343 - Load SDO data from current slave |
|
344 @param event : wx.EVT_BUTTON object |
|
345 """ |
|
346 self.Controler.CommonMethod.SaveSDOData = [] |
|
347 self.Controler.CommonMethod.ClearSDODataSet() |
|
348 self.SDOFlag = False |
|
349 |
|
350 # Check whether beremiz connected or not. |
|
351 check_connect_flag = self.Controler.CommonMethod.CheckConnect(False) |
|
352 if check_connect_flag: |
|
353 self.SDOs = self.Controler.CommonMethod.GetSlaveSDOFromSlave() |
|
354 # SDOFlag is "False", user click "Cancel" button |
|
355 self.SDOFlag = self.SDOParser() |
|
356 |
|
357 if self.SDOFlag : |
|
358 self.CallSDONoteBook.CreateNoteBook() |
|
359 self.Refresh() |
|
360 |
|
361 def SDOParser(self): |
|
362 """ |
|
363 Parse SDO data set that obtain "SDOInfoUpdate" Method |
|
364 @return True or False |
|
365 """ |
|
366 |
|
367 slaveSDO_progress = wx.ProgressDialog("Slave SDO Monitoring", "Now Uploading...", |
|
368 maximum = len(self.SDOs.splitlines()), parent=self, |
|
369 style = wx.PD_CAN_ABORT | wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | |
|
370 wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME | |
|
371 wx.PD_AUTO_HIDE | wx.PD_SMOOTH) |
|
372 |
|
373 # If keep_going flag is False, SDOParser method is stop and return "False". |
|
374 keep_going = True |
|
375 count = 0 |
|
376 |
|
377 # SDO data example |
|
378 # SDO 0x1000, "Device type" |
|
379 # 0x1000:00,r-r-r-,uint32,32 bit,"Device type",0x00020192, 131474 |
|
380 for details_line in self.SDOs.splitlines(): |
|
381 count += 1 |
|
382 line_token = details_line.split("\"") |
|
383 # len(line_token[2]) case : SDO 0x1000, "Device type" |
|
384 if len(line_token[2]) == 0: |
|
385 title_name = line_token[1] |
|
386 # else case : 0x1000:00,r-r-r-,uint32,32 bit,"Device type",0x00020192, 131474 |
|
387 else : |
|
388 # line_token = ['0x1000:00,r-r-r-,uint32,32 bit,', 'Device type', ',0x00020192, 131474'] |
|
389 token_head, name, token_tail = line_token |
|
390 |
|
391 # token_head = ['0x1000:00', 'r-r-r-', 'uint32', '32 bit', ''] |
|
392 token_head = token_head.split(",") |
|
393 ful_idx, access, type, size, empty = token_head |
|
394 # ful_idx.split(":") = ['0x1000', '00'] |
|
395 idx, sub_idx = ful_idx.split(":") |
|
396 |
|
397 # token_tail = ['', '0x00020192', '131474'] |
|
398 token_tail = token_tail.split(",") |
|
399 try : |
|
400 empty, hex_val, dec_val = token_tail |
|
401 |
|
402 # SDO data is not return "dec value" |
|
403 # line example : |
|
404 # 0x1702:01,rwr-r-,uint32,32 bit," 1st mapping", ---- |
|
405 except : |
|
406 empty, hex_val = token_tail |
|
407 |
|
408 name_after_check = self.StringTest(name) |
|
409 |
|
410 # convert hex type |
|
411 sub_idx = "0x" + sub_idx |
|
412 |
|
413 if type == "octet_string": |
|
414 hex_val = ' ---- ' |
|
415 |
|
416 # SResult of SlaveSDO data parsing. (data type : dictionary) |
|
417 self.Data = {'idx':idx.strip(), 'subIdx':sub_idx.strip(), 'access':access.strip(), |
|
418 'type':type.strip(), 'size':size.strip(), 'name':name_after_check.strip("\""), |
|
419 'value':hex_val.strip(), "category":title_name.strip("\"")} |
|
420 |
|
421 category_divide_value = [0x1000, 0x2000, 0x6000, 0xa000, 0xffff] |
|
422 |
|
423 for count in range(len(category_divide_value)) : |
|
424 if int(idx, 0) < category_divide_value[count]: |
|
425 self.Controler.CommonMethod.SaveSDOData[count].append(self.Data) |
|
426 break |
|
427 |
|
428 self.Controler.CommonMethod.SaveSDOData[self.AllSDOData].append(self.Data) |
|
429 |
|
430 if count >= len(self.SDOs.splitlines()) / 2: |
|
431 (keep_going, skip) = slaveSDO_progress.Update(count, "Please waiting a moment!!") |
|
432 else: |
|
433 (keep_going, skip) = slaveSDO_progress.Update(count) |
|
434 |
|
435 # If user click "Cancel" loop suspend immediately |
|
436 if (keep_going == False): |
|
437 break |
|
438 |
|
439 slaveSDO_progress.Destroy() |
|
440 return keep_going |
|
441 |
|
442 def StringTest(self, check_string): |
|
443 """ |
|
444 Test value 'name' is alphanumeric |
|
445 @param check_string : input data for check |
|
446 @return result : output data after check |
|
447 """ |
|
448 # string.printable is print this result |
|
449 #'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ |
|
450 #!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c |
|
451 allow_range = string.printable |
|
452 result = check_string |
|
453 for i in range(0, len(check_string)): |
|
454 # string.isalnum() is check whether string is alphanumeric or not |
|
455 if check_string[len(check_string)-1-i:len(check_string)-i] in allow_range : |
|
456 result = check_string[:len(check_string) - i] |
|
457 break |
|
458 return result |
|
459 |
|
460 |
|
461 #------------------------------------------------------------------------------- |
|
462 # For SDO Notebook (divide category) |
|
463 #------------------------------------------------------------------------------- |
|
464 class SDONoteBook(wx.Notebook): |
|
465 def __init__(self, parent, controler): |
|
466 """ |
|
467 Constructor |
|
468 @param parent: Reference to the parent SDOPanelClass class |
|
469 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
470 """ |
|
471 wx.Notebook.__init__(self, parent, id = -1, size=(850,500)) |
|
472 self.Controler = controler |
|
473 self.parent = parent |
|
474 |
|
475 self.CreateNoteBook() |
|
476 |
|
477 self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGED, self.OnPageChanged) |
|
478 self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGING, self.OnPageChanging) |
|
479 |
|
480 def CreateNoteBook(self): |
|
481 """ |
|
482 Create each NoteBook page, divided SDO index |
|
483 According to EtherCAT Communication(03/2011), 158p |
|
484 """ |
|
485 self.Data = [] |
|
486 count = 1 |
|
487 |
|
488 page_texts = [("all", self.parent.AllSDOData), |
|
489 ("0x0000 - 0x0ff", self.parent.DatatypeDescription), |
|
490 ("0x1000 - 0x1fff", self.parent.CommunicationObject), |
|
491 ("0x2000 - 0x5fff", self.parent.ManufacturerSpecific), |
|
492 ("0x6000 - 0x9fff", self.parent.ProfileSpecific), |
|
493 ("0xa000 - 0xffff", self.parent.Reserved)] |
|
494 |
|
495 page_tooltip_string = ["SDO Index 0x0000 - 0x0fff : Data Type Description", |
|
496 "SDO Index 0x1000 - 0x1fff : Communication object", |
|
497 "SDO Index 0x2000 - 0x5fff : Manufacturer specific", |
|
498 "SDO Index 0x6000 - 0x9fff : Profile specific", |
|
499 "SDO Index 0xa000 - 0xffff : Reserved", |
|
500 "All SDO Object"] |
|
501 |
|
502 self.DeleteAllPages() |
|
503 |
|
504 for txt, count in page_texts: |
|
505 self.Data = self.Controler.CommonMethod.SaveSDOData[count] |
|
506 self.Win = SlaveSDOTable(self, self.Data) |
|
507 self.AddPage(self.Win, txt) |
|
508 |
|
509 def OnPageChanged(self, event): |
|
510 old = event.GetOldSelection() |
|
511 new = event.GetSelection() |
|
512 sel = self.GetSelection() |
|
513 event.Skip() |
|
514 |
|
515 def OnPageChanging(self, event): |
|
516 old = event.GetOldSelection() |
|
517 new = event.GetSelection() |
|
518 sel = self.GetSelection() |
|
519 event.Skip() |
|
520 |
|
521 #------------------------------------------------------------------------------- |
|
522 # For SDO Grid (fill index, subindex, etc...) |
|
523 #------------------------------------------------------------------------------- |
|
524 class SlaveSDOTable(wx.grid.Grid): |
|
525 def __init__(self, parent, data): |
|
526 """ |
|
527 Constructor |
|
528 @param parent: Reference to the parent SDOPanelClass class |
|
529 @param data: SDO data after parsing "SDOParser" method |
|
530 """ |
|
531 wx.grid.Grid.__init__(self, parent, -1, size=(830,490), |
|
532 style=wx.EXPAND|wx.ALIGN_CENTRE_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) |
|
533 |
|
534 self.Controler = parent.Controler |
|
535 self.parent = parent |
|
536 self.SDOFlag = True |
|
537 if data is None : |
|
538 self.SDOs = [] |
|
539 else : |
|
540 self.SDOs = data |
|
541 |
|
542 self.CreateGrid(len(self.SDOs), 8) |
|
543 SDOCellSize = [(0, 65), (1, 65), (2, 50), (3, 55), |
|
544 (4, 40), (5, 200), (6, 250), (7, 85)] |
|
545 |
|
546 for (index, size) in SDOCellSize: |
|
547 self.SetColSize(index, size) |
|
548 |
|
549 self.SetRowLabelSize(0) |
|
550 |
|
551 SDOTableLabel = [(0, "Index"), (1, "Subindex"), (2, "Access"), |
|
552 (3, "Type"), (4, "Size"), (5, "Category"), |
|
553 (6, "Name"), (7, "Value")] |
|
554 |
|
555 for (index, label) in SDOTableLabel: |
|
556 self.SetColLabelValue(index, label) |
|
557 self.SetColLabelAlignment(index, wx.ALIGN_CENTRE) |
|
558 |
|
559 attr = wx.grid.GridCellAttr() |
|
560 |
|
561 # for SDO download |
|
562 self.Bind(gridlib.EVT_GRID_CELL_LEFT_DCLICK, self.SDOModifyDialog) |
|
563 |
|
564 for i in range(7): |
|
565 self.SetColAttr(i,attr) |
|
566 |
|
567 self.SetColLabelAlignment(wx.ALIGN_CENTER, wx.ALIGN_CENTER) |
|
568 |
|
569 self.SetTableValue() |
|
570 |
|
571 def SetTableValue(self): |
|
572 """ |
|
573 Cell is filled by new parsing data |
|
574 """ |
|
575 sdo_list = ['idx', 'subIdx', 'access', 'type', 'size', 'category', 'name', 'value'] |
|
576 for row_idx in range(len(self.SDOs)): |
|
577 for col_idx in range(len(self.SDOs[row_idx])): |
|
578 self.SetCellValue(row_idx, col_idx, self.SDOs[row_idx][sdo_list[col_idx]]) |
|
579 self.SetReadOnly(row_idx, col_idx, True) |
|
580 if col_idx < 5 : |
|
581 self.SetCellAlignment(row_idx, col_idx, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE) |
|
582 |
|
583 def CheckSDODataAccess(self, row): |
|
584 """ |
|
585 CheckSDODataAccess method is checking that access data has "w" |
|
586 Access field consist 6 char, if mean |
|
587 rw rw rw |
|
588 (preop) (safeop) (op) |
|
589 Example Access field : rwrwrw, rwrw-- |
|
590 @param row : Selected cell by user |
|
591 @return Write_flag : If data has "w", flag is true |
|
592 """ |
|
593 write_flag = False |
|
594 check = self.SDOs[row]['access'] |
|
595 if check[1:2] == 'w' : |
|
596 self.Controler.CommonMethod.Check_PREOP = True |
|
597 write_flag = True |
|
598 if check[3:4] == 'w' : |
|
599 self.Controler.CommonMethod.Check_SAFEOP = True |
|
600 write_flag = True |
|
601 if check[5:] =='w' : |
|
602 self.Controler.CommonMethod.Check_OP = True |
|
603 write_flag = True |
|
604 |
|
605 return write_flag |
|
606 |
|
607 def DecideSDODownload(self, state): |
|
608 """ |
|
609 compare current state and "access" field, |
|
610 result notify SDOModifyDialog method |
|
611 @param state : current slave state |
|
612 @return True or False |
|
613 """ |
|
614 # Example of 'state' parameter : "0 0:0 PREOP + EL9800 (V4.30) (PIC24, SPI, ET1100)" |
|
615 state = state[self.Controler.GetSlavePos()].split(" ")[2] |
|
616 if state == "PREOP" and self.Controler.CommonMethod.Check_PREOP : |
|
617 return True |
|
618 elif state == "SAFEOP" and self.Controler.CommonMethod.Check_SAFEOP : |
|
619 return True |
|
620 elif state == "OP" and self.Controler.CommonMethod.Check_OP : |
|
621 return True |
|
622 |
|
623 return False |
|
624 |
|
625 def ClearStateFlag(self): |
|
626 """ |
|
627 Initialize StateFlag |
|
628 StateFlag is notice SDOData access each slave state |
|
629 """ |
|
630 self.Controler.CommonMethod.Check_PREOP = False |
|
631 self.Controler.CommonMethod.Check_SAFEOP = False |
|
632 self.Controler.CommonMethod.Check_OP = False |
|
633 |
|
634 def SDOModifyDialog (self, event): |
|
635 """ |
|
636 Create dialog for SDO value modify |
|
637 if user enter data, perform command "ethercat download" |
|
638 @param event : gridlib.EVT_GRID_CELL_LEFT_DCLICK object |
|
639 """ |
|
640 self.ClearStateFlag() |
|
641 |
|
642 # CheckSDODataAccess is checking that OD(Object Dictionary) has "w" |
|
643 if event.GetCol() == 7 and self.CheckSDODataAccess(event.GetRow()) : |
|
644 dlg = wx.TextEntryDialog (self, "Enter hex or dec value (if enter dec value, it automatically conversed hex value)", |
|
645 "SDOModifyDialog", style = wx.OK | wx.CANCEL) |
|
646 |
|
647 start_value = self.GetCellValue(event.GetRow(), event.GetCol()) |
|
648 dlg.SetValue(start_value) |
|
649 |
|
650 if dlg.ShowModal() == wx.ID_OK: |
|
651 try : |
|
652 int(dlg.GetValue(), 0) |
|
653 # check "Access" field |
|
654 if self.DecideSDODownload(self.Controler.CommonMethod.SlaveState[self.Controler.GetSlavePos()]) : |
|
655 # Request "SDODownload" |
|
656 self.Controler.CommonMethod.SDODownload(self.SDOs[event.GetRow()]['type'], self.SDOs[event.GetRow()]['idx'], |
|
657 self.SDOs[event.GetRow()]['subIdx'], dlg.GetValue()) |
|
658 self.SetCellValue(event.GetRow(), event.GetCol(), hex(int(dlg.GetValue(), 0))) |
|
659 else : |
|
660 self.Controler.CommonMethod.CreateErrorDialog('You cannot SDO download this state') |
|
661 # Error occured process of "int(variable)" |
|
662 # User input is not hex, dec value |
|
663 except ValueError: |
|
664 self.Controler.CommonMethod.CreateErrorDialog('You can input only hex, dec value') |
|
665 |
|
666 |
|
667 #------------------------------------------------------------------------------- |
|
668 # For PDO Monitoring Panel |
|
669 # PDO Class UI : Panel -> Choicebook (RxPDO, TxPDO) -> |
|
670 # Notebook (PDO Index) -> Grid (PDO entry) |
|
671 #------------------------------------------------------------------------------- |
|
672 class PDOPanelClass(wx.Panel): |
|
673 def __init__(self, parent, controler): |
|
674 """ |
|
675 Constructor |
|
676 @param parent: Reference to the parent EtherCATManagementTreebook class |
|
677 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
678 """ |
|
679 wx.Panel.__init__(self, parent, -1) |
|
680 self.Controler = controler |
|
681 |
|
682 self.PDOMonitoringEditorMainSizer = wx.BoxSizer(wx.VERTICAL) |
|
683 self.PDOMonitoringEditorInnerMainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=10) |
|
684 |
|
685 self.CallPDOChoicebook = PDOChoicebook(self, controler=self.Controler) |
|
686 self.PDOMonitoringEditorInnerMainSizer.Add(self.CallPDOChoicebook, wx.ALL) |
|
687 |
|
688 self.PDOMonitoringEditorMainSizer.Add(self.PDOMonitoringEditorInnerMainSizer) |
|
689 |
|
690 self.SetSizer(self.PDOMonitoringEditorMainSizer) |
|
691 |
|
692 def PDOInfoUpdate(self): |
|
693 """ |
|
694 Call RequestPDOInfo method and create Choicebook |
|
695 """ |
|
696 self.Controler.CommonMethod.RequestPDOInfo() |
|
697 self.CallPDOChoicebook.Destroy() |
|
698 self.CallPDOChoicebook = PDOChoicebook(self, controler=self.Controler) |
|
699 self.Refresh() |
|
700 |
|
701 |
|
702 #------------------------------------------------------------------------------- |
|
703 # For PDO Choicebook (divide Tx, Rx PDO) |
|
704 #------------------------------------------------------------------------------- |
|
705 class PDOChoicebook(wx.Choicebook): |
|
706 def __init__(self, parent, controler): |
|
707 """ |
|
708 Constructor |
|
709 @param parent: Reference to the parent PDOPanelClass class |
|
710 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
711 """ |
|
712 wx.Choicebook.__init__(self, parent, id=-1, size=(500, 500), style=wx.CHB_DEFAULT) |
|
713 self.Controler = controler |
|
714 |
|
715 RxWin = PDONoteBook(self, controler=self.Controler, name="Rx") |
|
716 TxWin = PDONoteBook(self, controler=self.Controler, name="Tx") |
|
717 self.AddPage(RxWin, "RxPDO") |
|
718 self.AddPage(TxWin, "TxPDO") |
|
719 |
|
720 self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGED, self.OnPageChanged) |
|
721 self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGING, self.OnPageChanging) |
|
722 |
|
723 def OnPageChanged(self, event): |
|
724 old = event.GetOldSelection() |
|
725 new = event.GetSelection() |
|
726 sel = self.GetSelection() |
|
727 event.Skip() |
|
728 |
|
729 def OnPageChanging(self, event): |
|
730 old = event.GetOldSelection() |
|
731 new = event.GetSelection() |
|
732 sel = self.GetSelection() |
|
733 event.Skip() |
|
734 |
|
735 |
|
736 #------------------------------------------------------------------------------- |
|
737 # For PDO Notebook (divide PDO index) |
|
738 #------------------------------------------------------------------------------- |
|
739 class PDONoteBook(wx.Notebook): |
|
740 def __init__(self, parent, name, controler): |
|
741 """ |
|
742 Constructor |
|
743 @param parent: Reference to the parent PDOChoicebook class |
|
744 @param name: identifier whether RxPDO or TxPDO |
|
745 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
746 """ |
|
747 wx.Notebook.__init__(self, parent, id=-1, size=(640, 400)) |
|
748 self.Controler = controler |
|
749 |
|
750 count = 0 |
|
751 page_texts = [] |
|
752 |
|
753 self.Controler.CommonMethod.RequestPDOInfo() |
|
754 |
|
755 if name == "Tx" : |
|
756 # obtain pdo_info and pdo_entry |
|
757 # pdo_info include (PDO index, name, number of entry) |
|
758 pdo_info = self.Controler.CommonMethod.GetTxPDOCategory() |
|
759 pdo_entry = self.Controler.CommonMethod.GetTxPDOInfo() |
|
760 for tmp in pdo_info : |
|
761 title = str(hex(tmp['pdo_index'])) |
|
762 page_texts.append(title) |
|
763 # RX PDO case |
|
764 else : |
|
765 pdo_info = self.Controler.CommonMethod.GetRxPDOCategory() |
|
766 pdo_entry = self.Controler.CommonMethod.GetRxPDOInfo() |
|
767 for tmp in pdo_info : |
|
768 title = str(hex(tmp['pdo_index'])) |
|
769 page_texts.append(title) |
|
770 |
|
771 # Add page depending on the number of pdo_info |
|
772 for txt in page_texts: |
|
773 win = PDOEntryTable(self, pdo_info, pdo_entry, count) |
|
774 self.AddPage(win, txt) |
|
775 count += 1 |
|
776 |
|
777 self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGED, self.OnPageChanged) |
|
778 self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGING, self.OnPageChanging) |
|
779 |
|
780 def OnPageChanged(self, event): |
|
781 old = event.GetOldSelection() |
|
782 new = event.GetSelection() |
|
783 sel = self.GetSelection() |
|
784 event.Skip() |
|
785 |
|
786 def OnPageChanging(self, event): |
|
787 old = event.GetOldSelection() |
|
788 new = event.GetSelection() |
|
789 sel = self.GetSelection() |
|
790 event.Skip() |
|
791 |
|
792 |
|
793 #------------------------------------------------------------------------------- |
|
794 # For PDO Grid (fill entry index, subindex etc...) |
|
795 #------------------------------------------------------------------------------- |
|
796 class PDOEntryTable(wx.grid.Grid): |
|
797 def __init__(self, parent, info, entry, count): |
|
798 """ |
|
799 Constructor |
|
800 @param parent: Reference to the parent PDONoteBook class |
|
801 @param info : data structure including entry index, sub index, name, length, type |
|
802 @param entry : data structure including index, name, entry number |
|
803 @param count : page number |
|
804 """ |
|
805 wx.grid.Grid.__init__(self, parent, -1, size=(500, 400), pos=wx.Point(0,0), |
|
806 style=wx.EXPAND|wx.ALIGN_CENTRE_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) |
|
807 |
|
808 self.Controler = parent.Controler |
|
809 |
|
810 self.PDOInfo = info |
|
811 self.PDOEntry = entry |
|
812 self.Count = count |
|
813 |
|
814 self.CreateGrid(self.PDOInfo[self.Count]['number_of_entry'], 5) |
|
815 self.SetColLabelSize(25) |
|
816 self.SetRowLabelSize(0) |
|
817 |
|
818 PDOTableLabel = [(0, "Index"), (1, "Subindex"), (2, "Length"), |
|
819 (3, "Type"), (4, "Name")] |
|
820 |
|
821 for (index, label) in PDOTableLabel: |
|
822 self.SetColLabelValue(index, label) |
|
823 |
|
824 PDOCellSize = [(0, 45), (1, 65), (2, 55), (3, 40), (4, 300)] |
|
825 |
|
826 for (index, size) in PDOCellSize: |
|
827 self.SetColSize(index, size) |
|
828 self.SetColLabelAlignment(index, wx.ALIGN_LEFT) |
|
829 |
|
830 attr = wx.grid.GridCellAttr() |
|
831 |
|
832 for i in range(5): |
|
833 self.SetColAttr(i, attr) |
|
834 |
|
835 self.SetTableValue() |
|
836 |
|
837 def SetTableValue(self): |
|
838 """ |
|
839 Cell is filled by new parsing data in XML |
|
840 """ |
|
841 list_index = 0 |
|
842 # number of entry |
|
843 for i in range(self.Count + 1) : |
|
844 list_index += self.PDOInfo[i]['number_of_entry'] |
|
845 |
|
846 start_value = list_index - self.PDOInfo[self.Count]['number_of_entry'] |
|
847 |
|
848 pdo_list = ['entry_index', 'subindex', 'bitlen', 'type', 'name'] |
|
849 for row_idx in range(self.PDOInfo[self.Count]['number_of_entry']): |
|
850 for col_idx in range(len(self.PDOEntry[row_idx])): |
|
851 # entry index is converted hex value. |
|
852 if col_idx == 0 : |
|
853 self.SetCellValue(row_idx, col_idx, hex(self.PDOEntry[start_value][pdo_list[col_idx]])) |
|
854 else : |
|
855 self.SetCellValue(row_idx, col_idx, str(self.PDOEntry[start_value][pdo_list[col_idx]])) |
|
856 if col_idx != 4 : |
|
857 self.SetCellAlignment(row_idx, col_idx, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE) |
|
858 else : |
|
859 self.SetCellAlignment(row_idx, col_idx, wx.ALIGN_LEFT, wx.ALIGN_CENTRE) |
|
860 self.SetReadOnly(row_idx, col_idx, True) |
|
861 self.SetRowSize(row_idx, 25) |
|
862 start_value += 1 |
|
863 |
|
864 |
|
865 #------------------------------------------------------------------------------- |
|
866 # For EEPROM Access Main Panel |
|
867 # (This class explain EEPROM Access) |
|
868 #------------------------------------------------------------------------------- |
|
869 class EEPROMAccessPanel(wx.Panel): |
|
870 def __init__(self, parent, controler): |
|
871 """ |
|
872 Constructor |
|
873 @param parent: Reference to the parent EtherCATManagementTreebook class |
|
874 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
875 """ |
|
876 wx.Panel.__init__(self, parent, -1) |
|
877 sizer = wx.FlexGridSizer(cols=1, hgap=20,rows=3, vgap=20) |
|
878 |
|
879 line = wx.StaticText(self, -1, "\n EEPROM Access is composed to SmartView and HexView. \ |
|
880 \n\n - SmartView shows Config Data, Device Identity, Mailbox settings, etc. \ |
|
881 \n\n - HexView shows EEPROM's contents.") |
|
882 |
|
883 sizer.Add(line) |
|
884 |
|
885 self.SetSizer(sizer) |
|
886 |
|
887 |
|
888 #------------------------------------------------------------------------------- |
|
889 # For Smart View Panel |
|
890 #------------------------------------------------------------------------------- |
|
891 class SlaveSiiSmartView(wx.Panel): |
|
892 def __init__(self, parent, controler): |
|
893 """ |
|
894 Constructor |
|
895 @param parent: Reference to the parent EtherCATManagementTreebook class |
|
896 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
897 """ |
|
898 wx.Panel.__init__(self, parent, -1) |
|
899 self.parent = parent |
|
900 self.Controler = controler |
|
901 |
|
902 self.PDIType = {0 :['none', '00000000'], |
|
903 4 :['Digital I/O', '00000100'], |
|
904 5 :['SPI Slave', '00000101'], |
|
905 7 :['EtherCAT Bridge (port3)', '00000111'], |
|
906 8 :['uC async. 16bit', '00001000'], |
|
907 9 :['uC async. 8bit', '00001001'], |
|
908 10 :['uC sync. 16bit', '00001010'], |
|
909 11 :['uC sync. 8bit', '00001011'], |
|
910 16 :['32 Digtal Input and 0 Digital Output', '00010000'], |
|
911 17 :['24 Digtal Input and 8 Digital Output', '00010001'], |
|
912 18 :['16 Digtal Input and 16 Digital Output','00010010'], |
|
913 19 :['8 Digtal Input and 24 Digital Output', '00010011'], |
|
914 20 :['0 Digtal Input and 32 Digital Output', '00010100'], |
|
915 128:['On-chip bus', '11111111'] |
|
916 } |
|
917 |
|
918 sizer = wx.FlexGridSizer(cols=1, hgap=5, rows=2, vgap=5) |
|
919 button_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=1, vgap=5) |
|
920 |
|
921 for button, mapping_method in [("Write EEPROM", self.WriteToEEPROM), |
|
922 ("Read EEPROM", self.ReadFromEEPROM)]: |
|
923 btn = wx.Button(self, -1, button, size=(150, 40)) |
|
924 button_sizer.Add(btn, border=10, flag=wx.ALL) |
|
925 btn.Bind(wx.EVT_BUTTON, mapping_method) |
|
926 |
|
927 self.TreeListCtrl = SmartViewTreeListCtrl(self, self.Controler) |
|
928 |
|
929 sizer.Add(button_sizer, border=10, flag=wx.ALL) |
|
930 sizer.Add(self.TreeListCtrl, border=10, flag=wx.ALL) |
|
931 self.SetSizer(sizer) |
|
932 |
|
933 self.Create_SmartView() |
|
934 |
|
935 def Create_SmartView(self): |
|
936 """ |
|
937 SmartView shows information based on XML as initial value. |
|
938 """ |
|
939 self.Controler.CommonMethod.SmartViewInfosFromXML = self.Controler.CommonMethod.GetSmartViewInfos() |
|
940 self.SetXMLData() |
|
941 |
|
942 def WriteToEEPROM(self, event): |
|
943 """ |
|
944 Open binary file (user select) and write the selected binary data to EEPROM |
|
945 @param event : wx.EVT_BUTTON object |
|
946 """ |
|
947 # Check whether beremiz connected or not, and whether status is "Started" or not. |
|
948 check_connect_flag = self.Controler.CommonMethod.CheckConnect(False) |
|
949 if check_connect_flag: |
|
950 status, count = self.Controler.GetCTRoot()._connector.GetPLCstatus() |
|
951 if status is not "Started": |
|
952 dialog = wx.FileDialog(self, _("Choose a binary file"), os.getcwd(), "", _("bin files (*.bin)|*.bin"), wx.OPEN) |
|
953 |
|
954 if dialog.ShowModal() == wx.ID_OK: |
|
955 filepath = dialog.GetPath() |
|
956 try: |
|
957 binfile = open(filepath,"rb") |
|
958 self.SiiBinary = binfile.read() |
|
959 dialog.Destroy() |
|
960 |
|
961 self.Controler.CommonMethod.SiiWrite(self.SiiBinary) |
|
962 # refresh data structure kept by master |
|
963 self.Controler.CommonMethod.Rescan() |
|
964 # save binary data as inner global data of beremiz |
|
965 # for fast loading when slave plugin node is reopened. |
|
966 self.Controler.CommonMethod.SiiData = self.SiiBinary |
|
967 self.SetEEPROMData() |
|
968 except: |
|
969 self.Controler.CommonMethod.CreateErrorDialog('The file does not exist!') |
|
970 dialog.Destroy() |
|
971 |
|
972 def ReadFromEEPROM(self, event): |
|
973 """ |
|
974 Refresh displayed data based on slave EEPROM and save binary file through dialog |
|
975 @param event : wx.EVT_BUTTON object |
|
976 """ |
|
977 # Check whether beremiz connected or not. |
|
978 check_connect_flag = self.Controler.CommonMethod.CheckConnect(False) |
|
979 if check_connect_flag: |
|
980 self.SiiBinary = self.Controler.CommonMethod.LoadData() |
|
981 self.SetEEPROMData() |
|
982 dialog = wx.FileDialog(self, _("Save as..."), os.getcwd(), |
|
983 "slave0.bin", _("bin files (*.bin)|*.bin|All files|*.*"), |
|
984 wx.SAVE|wx.OVERWRITE_PROMPT) |
|
985 |
|
986 if dialog.ShowModal() == wx.ID_OK: |
|
987 filepath = dialog.GetPath() |
|
988 binfile = open(filepath,"wb") |
|
989 binfile.write(self.SiiBinary) |
|
990 binfile.close() |
|
991 |
|
992 dialog.Destroy() |
|
993 |
|
994 def SetXMLData(self): |
|
995 """ |
|
996 Set data based on XML initially |
|
997 """ |
|
998 # Config Data: EEPROM Size, PDI Type, Device Emulation |
|
999 # Find PDI Type in pdiType dictionary |
|
1000 cnt_pdi_type = self.Controler.CommonMethod.SmartViewInfosFromXML["pdi_type"] |
|
1001 for i in self.PDIType.keys(): |
|
1002 if cnt_pdi_type == i: |
|
1003 cnt_pdi_type = self.PDIType[i][0] |
|
1004 break |
|
1005 # Set Config Data |
|
1006 for treelist, data in [("EEPROM Size (Bytes)", |
|
1007 str(self.Controler.CommonMethod.SmartViewInfosFromXML["eeprom_size"])), |
|
1008 ("PDI Type", |
|
1009 cnt_pdi_type), |
|
1010 ("Device Emulation", |
|
1011 self.Controler.CommonMethod.SmartViewInfosFromXML["device_emulation"])]: |
|
1012 self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.ConfigData[treelist], data, 1) |
|
1013 |
|
1014 # Device Identity: Vendor ID, Product Code, Revision No., Serial No. |
|
1015 # Set Device Identity |
|
1016 for treelist, data in [("Vendor ID", self.Controler.CommonMethod.SmartViewInfosFromXML["vendor_id"]), |
|
1017 ("Product Code", self.Controler.CommonMethod.SmartViewInfosFromXML["product_code"]), |
|
1018 ("Revision No.", self.Controler.CommonMethod.SmartViewInfosFromXML["revision_no"]), |
|
1019 ("Serial No.", self.Controler.CommonMethod.SmartViewInfosFromXML["serial_no"])]: |
|
1020 self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.DeviceIdentity[treelist], data, 1) |
|
1021 |
|
1022 # Mailbox: Supported Mailbox, Bootstrap Configuration, Standard Configuration |
|
1023 # Set Mailbox |
|
1024 for treelist, data in [("Supported Mailbox", self.Controler.CommonMethod.SmartViewInfosFromXML["supported_mailbox"]), |
|
1025 ("Bootstrap Configuration", ""), |
|
1026 ("Standard Configuration", "")]: |
|
1027 self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.Mailbox[treelist], data, 1) |
|
1028 # Set Bootstrap Configuration: Receive Offset, Receive Size, Send Offset, Send Size |
|
1029 for treelist, data in [("Receive Offset", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_bootstrapconf_outstart"]), |
|
1030 ("Receive Size", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_bootstrapconf_outlength"]), |
|
1031 ("Send Offset", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_bootstrapconf_instart"]), |
|
1032 ("Send Size", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_bootstrapconf_inlength"])]: |
|
1033 self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.BootstrapConfig[treelist], data, 1) |
|
1034 # Set Standard Configuration: Receive Offset, Receive Size, Send Offset, Send Size |
|
1035 for treelist, data in [("Receive Offset", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_standardconf_outstart"]), |
|
1036 ("Receive Size", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_standardconf_outlength"]), |
|
1037 ("Send Offset", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_standardconf_instart"]), |
|
1038 ("Send Size", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_standardconf_inlength"])]: |
|
1039 self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.StandardConfig[treelist], data, 1) |
|
1040 |
|
1041 def SetEEPROMData(self): |
|
1042 """ |
|
1043 Set data based on slave EEPROM. |
|
1044 """ |
|
1045 # sii_dict = { Parameter : (WordAddress, WordSize) } |
|
1046 sii_dict= { 'PDIControl' : ( '0', 1), |
|
1047 'PDIConfiguration' : ( '1', 1), |
|
1048 'PulseLengthOfSYNCSignals' : ( '2', 1), |
|
1049 'ExtendedPDIConfiguration' : ( '3', 1), |
|
1050 'ConfiguredStationAlias' : ( '4', 1), |
|
1051 'Checksum' : ( '7', 1), |
|
1052 'VendorID' : ( '8', 2), |
|
1053 'ProductCode' : ( 'a', 2), |
|
1054 'RevisionNumber' : ( 'c', 2), |
|
1055 'SerialNumber' : ( 'e', 2), |
|
1056 'Execution Delay' : ('10', 1), |
|
1057 'Port0Delay' : ('11', 1), |
|
1058 'Port1Delay' : ('12', 1), |
|
1059 'BootstrapReceiveMailboxOffset' : ('14', 1), |
|
1060 'BootstrapReceiveMailboxSize' : ('15', 1), |
|
1061 'BootstrapSendMailboxOffset' : ('16', 1), |
|
1062 'BootstrapSendMailboxSize' : ('17', 1), |
|
1063 'StandardReceiveMailboxOffset' : ('18', 1), |
|
1064 'StandardReceiveMailboxSize' : ('19', 1), |
|
1065 'StandardSendMailboxOffset' : ('1a', 1), |
|
1066 'StandardSendMailboxSize' : ('1b', 1), |
|
1067 'MailboxProtocol' : ('1c', 1), |
|
1068 'Size' : ('3e', 1), |
|
1069 'Version' : ('3f', 1), |
|
1070 'First Category Type/Vendor Specific' : ('40', 1), |
|
1071 'Following Category Word Size' : ('41', 1), |
|
1072 'Category Data' : ('42', 1), |
|
1073 } |
|
1074 |
|
1075 # Config Data: EEPROM Size, PDI Type, Device Emulation |
|
1076 # EEPROM's data in address '0x003f' is Size of EEPROM in KBit-1 |
|
1077 eeprom_size = str((int(self.GetWordAddressData( sii_dict.get('Size'),10 ))+1)/8*1024) |
|
1078 # Find PDI Type in pdiType dictionary |
|
1079 cnt_pdi_type = int(self.GetWordAddressData( sii_dict.get('PDIControl'),16 ).split('x')[1][2:4], 16) |
|
1080 for i in self.PDIType.keys(): |
|
1081 if cnt_pdi_type == i: |
|
1082 cnt_pdi_type = self.PDIType[i][0] |
|
1083 break |
|
1084 # Get Device Emulation |
|
1085 device_emulation = str(bool(int("{:0>16b}".format(int(self.GetWordAddressData( sii_dict.get('PDIControl'),16 ), 16))[7]))) |
|
1086 # Set Config Data |
|
1087 for treelist, data in [("EEPROM Size (Bytes)", eeprom_size), |
|
1088 ("PDI Type", cnt_pdi_type), |
|
1089 ("Device Emulation", device_emulation)]: |
|
1090 self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.ConfigData[treelist], data, 1) |
|
1091 |
|
1092 # Device Identity: Vendor ID, Product Code, Revision No., Serial No. |
|
1093 # Set Device Identity |
|
1094 for treelist, data in [("Vendor ID", self.GetWordAddressData( sii_dict.get('VendorID'),16 )), |
|
1095 ("Product Code", self.GetWordAddressData( sii_dict.get('ProductCode'),16 )), |
|
1096 ("Revision No.", self.GetWordAddressData( sii_dict.get('RevisionNumber'),16 )), |
|
1097 ("Serial No.", self.GetWordAddressData( sii_dict.get('SerialNumber'),16 ))]: |
|
1098 self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.DeviceIdentity[treelist], data, 1) |
|
1099 |
|
1100 # Mailbox |
|
1101 # EEORPOM's word address '1c' indicates supported mailbox protocol. |
|
1102 # each value of mailbox protocol : |
|
1103 # VoE(0x0020), SoE(0x0010), FoE(0x0008), CoE(0x0004), EoE(0x0002), AoE(0x0001) |
|
1104 supported_mailbox = "" |
|
1105 mailbox_protocol=["VoE, ", "SoE, ", "FoE, ", "CoE, ", "EoE, ", "AoE, "] |
|
1106 mailbox_data = "{:0>8b}".format(int(self.GetWordAddressData( sii_dict.get('MailboxProtocol'),16 ), 16)) |
|
1107 for protocol in range(6): |
|
1108 if mailbox_data[protocol+2] == '1': |
|
1109 supported_mailbox += mailbox_protocol[protocol] |
|
1110 supported_mailbox = supported_mailbox.strip(", ") |
|
1111 # Set Mailbox |
|
1112 for treelist, data in [("Supported Mailbox", supported_mailbox), |
|
1113 ("Bootstrap Configuration", ""), |
|
1114 ("Standard Configuration", "")]: |
|
1115 self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.Mailbox[treelist], data, 1) |
|
1116 # Set Bootstrap Configuration: Receive Offset, Receive Size, Send Offset, Send Size |
|
1117 for treelist, data in [("Receive Offset", self.GetWordAddressData( sii_dict.get('BootstrapReceiveMailboxOffset'),10 )), |
|
1118 ("Receive Size", self.GetWordAddressData( sii_dict.get('BootstrapReceiveMailboxSize'),10 )), |
|
1119 ("Send Offset", self.GetWordAddressData( sii_dict.get('BootstrapSendMailboxOffset'),10 )), |
|
1120 ("Send Size", self.GetWordAddressData( sii_dict.get('BootstrapSendMailboxSize'),10 ))]: |
|
1121 self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.BootstrapConfig[treelist], data, 1) |
|
1122 # Set Standard Configuration: Receive Offset, Receive Size, Send Offset, Send Size |
|
1123 for treelist, data in [("Receive Offset", self.GetWordAddressData( sii_dict.get('StandardReceiveMailboxOffset'),10 )), |
|
1124 ("Receive Size", self.GetWordAddressData( sii_dict.get('StandardReceiveMailboxSize'),10 )), |
|
1125 ("Send Offset", self.GetWordAddressData( sii_dict.get('StandardSendMailboxOffset'),10 )), |
|
1126 ("Send Size", self.GetWordAddressData( sii_dict.get('StandardSendMailboxSize'),10 ))]: |
|
1127 self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.StandardConfig[treelist], data, 1) |
|
1128 |
|
1129 def MakeStaticBoxSizer(self, boxlabel): |
|
1130 """ |
|
1131 Make StaticBoxSizer |
|
1132 @param boxlabel : label of box sizer |
|
1133 @return sizer : the StaticBoxSizer labeled 'boxlabel' |
|
1134 """ |
|
1135 box = wx.StaticBox(self, -1, boxlabel) |
|
1136 sizer = wx.StaticBoxSizer(box, wx.VERTICAL) |
|
1137 |
|
1138 return sizer |
|
1139 |
|
1140 def GetWordAddressData(self, dict_tuple, format): |
|
1141 """ |
|
1142 This method converts word address data from EEPROM binary. |
|
1143 @param dict_tuple : element of 'sii_dict' dictionary in SetEEPROMData() |
|
1144 @param format : format of data. It can be 16(hex), 10(decimal) and 2(binary). |
|
1145 @return formatted value |
|
1146 """ |
|
1147 offset = int(str(dict_tuple[0]), 16) * 2 |
|
1148 length = int(str(dict_tuple[1]), 16) * 2 |
|
1149 list = [] |
|
1150 data = '' |
|
1151 for index in range(length): |
|
1152 hexdata = hex(ord(self.SiiBinary[offset + index]))[2:] |
|
1153 list.append(hexdata.zfill(2)) |
|
1154 |
|
1155 list.reverse() |
|
1156 data = list[0:length] |
|
1157 |
|
1158 if format == 16: |
|
1159 return '0x' + ''.join(data) |
|
1160 elif format == 10: |
|
1161 return str(int(str(''.join(data)), 16)) |
|
1162 elif format == 2: |
|
1163 ''.join(data) |
|
1164 |
|
1165 |
|
1166 #------------------------------------------------------------------------------- |
|
1167 # For Smart View TreeListCtrl |
|
1168 #------------------------------------------------------------------------------- |
|
1169 class SmartViewTreeListCtrl(wx.Panel): |
|
1170 def __init__(self, parent, Controler): |
|
1171 """ |
|
1172 Constructor |
|
1173 @param parent: Reference to the parent SlaveSiiSmartView class |
|
1174 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
1175 """ |
|
1176 |
|
1177 wx.Panel.__init__(self, parent, -1, size=(350, 500)) |
|
1178 |
|
1179 self.Tree = wx.gizmos.TreeListCtrl(self, -1, size=(350, 500), |
|
1180 style=wx.TR_DEFAULT_STYLE |
|
1181 |wx.TR_FULL_ROW_HIGHLIGHT |
|
1182 |wx.TR_HIDE_ROOT |
|
1183 |wx.TR_COLUMN_LINES |
|
1184 |wx.TR_ROW_LINES) |
|
1185 |
|
1186 self.Tree.AddColumn("Description", width=200) |
|
1187 self.Tree.AddColumn("Value", width=140) |
|
1188 self.Tree.SetMainColumn(0) |
|
1189 |
|
1190 self.Root = self.Tree.AddRoot("") |
|
1191 |
|
1192 # Add item |
|
1193 # Level 1 nodes |
|
1194 self.Level1Nodes = {} |
|
1195 for lv1 in ["Config Data", "Device Identity", "Mailbox"]: |
|
1196 self.Level1Nodes[lv1] = self.Tree.AppendItem(self.Root, lv1) |
|
1197 |
|
1198 # Level 2 nodes |
|
1199 # Config Data |
|
1200 self.ConfigData = {} |
|
1201 for lv2 in ["EEPROM Size (Bytes)", "PDI Type", "Device Emulation"]: |
|
1202 self.ConfigData[lv2] = self.Tree.AppendItem(self.Level1Nodes["Config Data"], lv2) |
|
1203 # Device Identity |
|
1204 self.DeviceIdentity = {} |
|
1205 for lv2 in ["Vendor ID", "Product Code", "Revision No.", "Serial No."]: |
|
1206 self.DeviceIdentity[lv2] = self.Tree.AppendItem(self.Level1Nodes["Device Identity"], lv2) |
|
1207 # Mailbox |
|
1208 self.Mailbox = {} |
|
1209 for lv2 in ["Supported Mailbox", "Bootstrap Configuration", "Standard Configuration"]: |
|
1210 self.Mailbox[lv2] = self.Tree.AppendItem(self.Level1Nodes["Mailbox"], lv2) |
|
1211 |
|
1212 # Level 3 nodes |
|
1213 # Children of Bootstrap Configuration |
|
1214 self.BootstrapConfig = {} |
|
1215 for lv3 in ["Receive Offset", "Receive Size", "Send Offset", "Send Size"]: |
|
1216 self.BootstrapConfig[lv3] = self.Tree.AppendItem(self.Mailbox["Bootstrap Configuration"], lv3) |
|
1217 # Children of Standard Configuration |
|
1218 self.StandardConfig = {} |
|
1219 for lv3 in ["Receive Offset", "Receive Size", "Send Offset", "Send Size"]: |
|
1220 self.StandardConfig[lv3] = self.Tree.AppendItem(self.Mailbox["Standard Configuration"], lv3) |
|
1221 |
|
1222 # Expand Tree |
|
1223 for tree in [self.Root, |
|
1224 self.Level1Nodes["Config Data"], |
|
1225 self.Level1Nodes["Device Identity"], |
|
1226 self.Level1Nodes["Mailbox"], |
|
1227 self.Mailbox["Bootstrap Configuration"], |
|
1228 self.Mailbox["Standard Configuration"]]: |
|
1229 self.Tree.Expand(tree) |
|
1230 |
|
1231 |
|
1232 #------------------------------------------------------------------------------- |
|
1233 # For Hex View Panel |
|
1234 # shows EEPROM binary as hex data and characters. |
|
1235 #------------------------------------------------------------------------------- |
|
1236 class HexView(wx.Panel): |
|
1237 def __init__(self, parent, controler): |
|
1238 """ |
|
1239 Constructor |
|
1240 @param parent: Reference to the parent EtherCATManagementTreebook class |
|
1241 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
1242 """ |
|
1243 wx.Panel.__init__(self, parent, -1) |
|
1244 self.parent = parent |
|
1245 self.Controler = controler |
|
1246 |
|
1247 self.HexRow = 8 |
|
1248 self.HexCol = 17 |
|
1249 |
|
1250 self.HexViewSizer = {"view" : wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=10), |
|
1251 "siiButton" : wx.BoxSizer()} |
|
1252 self.HexViewButton = {} |
|
1253 |
|
1254 for key, evt_handler in [("Sii Upload", self.OnButtonSiiUpload), |
|
1255 ("Sii Download", self.OnButtonSiiDownload), |
|
1256 ("Write to File", self.OnButtonWriteToBinFile), |
|
1257 ("Read from File", self.OnButtonReadFromBinFile), |
|
1258 ("XML to EEPROM Image", self.OnButtonXmlToEEPROMImg)]: |
|
1259 self.HexViewButton[key] = wx.Button(self, -1, key) |
|
1260 self.HexViewButton[key].Bind(wx.EVT_BUTTON, evt_handler) |
|
1261 self.HexViewSizer["siiButton"].Add(self.HexViewButton[key]) |
|
1262 |
|
1263 self.SiiBinary = self.Controler.CommonMethod.XmlToEeprom() |
|
1264 self.HexCode, self.HexRow, self.HexCol = self.Controler.CommonMethod.HexRead(self.SiiBinary) |
|
1265 self.SiiGrid = SiiGridTable(self, self.Controler, self.HexRow, self.HexCol) |
|
1266 self.HexViewSizer["view"].AddMany([self.HexViewSizer["siiButton"], self.SiiGrid]) |
|
1267 self.SiiGrid.CreateGrid(self.HexRow, self.HexCol) |
|
1268 self.SetSizer(self.HexViewSizer["view"]) |
|
1269 self.HexViewSizer["view"].FitInside(self.parent.parent) |
|
1270 self.parent.parent.FitInside() |
|
1271 self.SiiGrid.SetValue(self.HexCode) |
|
1272 self.SiiGrid.Update() |
|
1273 |
|
1274 def UpdateSiiGridTable(self, row, col): |
|
1275 """ |
|
1276 Destroy existing grid and recreate |
|
1277 @param row, col : Hex View grid size |
|
1278 """ |
|
1279 self.HexViewSizer["view"].Detach(self.SiiGrid) |
|
1280 self.SiiGrid.Destroy() |
|
1281 self.SiiGrid = SiiGridTable(self, self.Controler, row, col) |
|
1282 self.HexViewSizer["view"].Add(self.SiiGrid) |
|
1283 self.SiiGrid.CreateGrid(row, col) |
|
1284 self.SetSizer(self.HexViewSizer["view"]) |
|
1285 self.HexViewSizer["view"].FitInside(self.parent.parent) |
|
1286 self.parent.parent.FitInside() |
|
1287 |
|
1288 def OnButtonSiiUpload(self, event): |
|
1289 """ |
|
1290 Load EEPROM data from slave and refresh Hex View grid |
|
1291 Binded to 'Sii Upload' button. |
|
1292 @param event : wx.EVT_BUTTON object |
|
1293 """ |
|
1294 # Check whether beremiz connected or not. |
|
1295 check_connect_flag = self.Controler.CommonMethod.CheckConnect(False) |
|
1296 if check_connect_flag: |
|
1297 # load from EEPROM data and parsing |
|
1298 self.SiiBinary = self.Controler.CommonMethod.LoadData() |
|
1299 self.HexCode, self.HexRow, self.HexCol = self.Controler.CommonMethod.HexRead(self.SiiBinary) |
|
1300 self.UpdateSiiGridTable(self.HexRow, self.HexCol) |
|
1301 self.SiiGrid.SetValue(self.HexCode) |
|
1302 self.SiiGrid.Update() |
|
1303 |
|
1304 def OnButtonSiiDownload(self, event): |
|
1305 """ |
|
1306 Write current EEPROM data to slave and refresh data structure kept by master |
|
1307 Binded to 'Sii Download' button. |
|
1308 @param event : wx.EVT_BUTTON object |
|
1309 """ |
|
1310 # Check whether beremiz connected or not, |
|
1311 # and whether status is "Started" or not. |
|
1312 check_connect_flag = self.Controler.CommonMethod.CheckConnect(False) |
|
1313 if check_connect_flag: |
|
1314 status, count = self.Controler.GetCTRoot()._connector.GetPLCstatus() |
|
1315 if status is not "Started": |
|
1316 self.Controler.CommonMethod.SiiWrite(self.SiiBinary) |
|
1317 self.Controler.CommonMethod.Rescan() |
|
1318 |
|
1319 def OnButtonWriteToBinFile(self, event): |
|
1320 """ |
|
1321 Save current EEPROM data to binary file through FileDialog |
|
1322 Binded to 'Write to File' button. |
|
1323 @param event : wx.EVT_BUTTON object |
|
1324 """ |
|
1325 dialog = wx.FileDialog(self, _("Save as..."), os.getcwd(), "slave0.bin", |
|
1326 _("bin files (*.bin)|*.bin|All files|*.*"), wx.SAVE|wx.OVERWRITE_PROMPT) |
|
1327 |
|
1328 if dialog.ShowModal() == wx.ID_OK: |
|
1329 filepath = dialog.GetPath() |
|
1330 binfile = open(filepath,"wb") |
|
1331 binfile.write(self.SiiBinary) |
|
1332 binfile.close() |
|
1333 |
|
1334 dialog.Destroy() |
|
1335 |
|
1336 def OnButtonReadFromBinFile(self, event): |
|
1337 """ |
|
1338 Load binary file through FileDialog |
|
1339 Binded to 'Read from File' button. |
|
1340 @param event : wx.EVT_BUTTON object |
|
1341 """ |
|
1342 dialog = wx.FileDialog(self, _("Choose a binary file"), os.getcwd(), "", |
|
1343 _("bin files (*.bin)|*.bin"), wx.OPEN) |
|
1344 |
|
1345 if dialog.ShowModal() == wx.ID_OK: |
|
1346 filepath = dialog.GetPath() |
|
1347 |
|
1348 try: |
|
1349 binfile = open(filepath, "rb") |
|
1350 self.SiiBinary = binfile.read() |
|
1351 self.HexCode, self.HexRow, self.HexCol = self.Controler.CommonMethod.HexRead(self.SiiBinary) |
|
1352 self.UpdateSiiGridTable(self.HexRow, self.HexCol) |
|
1353 self.SiiGrid.SetValue(self.HexCode) |
|
1354 self.SiiGrid.Update() |
|
1355 except: |
|
1356 self.Controler.CommonMethod.CreateErrorDialog('The file does not exist!') |
|
1357 |
|
1358 dialog.Destroy() |
|
1359 |
|
1360 def OnButtonXmlToEEPROMImg(self, event): |
|
1361 """ |
|
1362 Create EEPROM data based XML data that current imported |
|
1363 Binded to 'XML to EEPROM' button. |
|
1364 @param event : wx.EVT_BUTTON object |
|
1365 """ |
|
1366 self.SiiBinary = self.Controler.CommonMethod.XmlToEeprom() |
|
1367 self.HexCode, self.HexRow, self.HexCol = self.Controler.CommonMethod.HexRead(self.SiiBinary) |
|
1368 self.UpdateSiiGridTable(self.HexRow, self.HexCol) |
|
1369 self.SiiGrid.SetValue(self.HexCode) |
|
1370 self.SiiGrid.Update() |
|
1371 |
|
1372 |
|
1373 #------------------------------------------------------------------------------- |
|
1374 # For Hex View grid (fill hex data) |
|
1375 #------------------------------------------------------------------------------- |
|
1376 class SiiGridTable(wx.grid.Grid): |
|
1377 def __init__(self, parent, controler, row, col): |
|
1378 """ |
|
1379 Constructor |
|
1380 @param parent: Reference to the parent HexView class |
|
1381 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
1382 @param row, col: Hex View grid size |
|
1383 """ |
|
1384 self.parent = parent |
|
1385 self.Controler = controler |
|
1386 self.Row = row |
|
1387 self.Col = col |
|
1388 |
|
1389 wx.grid.Grid.__init__(self, parent, -1, size=(830,450), |
|
1390 style=wx.ALIGN_CENTRE_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) |
|
1391 |
|
1392 def SetValue(self, value): |
|
1393 """ |
|
1394 Set data in the table |
|
1395 @param value: EEPROM data list of which element is 1 Byte hex data |
|
1396 """ |
|
1397 # set label name and size |
|
1398 self.SetRowLabelSize(100) |
|
1399 for col in range(self.Col): |
|
1400 if col == 16: |
|
1401 self.SetColLabelValue(16, "Text View") |
|
1402 self.SetColSize(16, (self.GetSize().x-120)*4/20) |
|
1403 else: |
|
1404 self.SetColLabelValue(col, '%s'%col) |
|
1405 self.SetColSize(col, (self.GetSize().x-120)/20) |
|
1406 |
|
1407 # set data into table |
|
1408 row = col = 0 |
|
1409 for row_idx in value: |
|
1410 col = 0 |
|
1411 self.SetRowLabelValue(row, "0x"+"{:0>4x}".format(row*(self.Col-1))) |
|
1412 for hex in row_idx: |
|
1413 self.SetCellValue(row, col, hex) |
|
1414 |
|
1415 if col == 16: |
|
1416 self.SetCellAlignment(row, col, wx.ALIGN_LEFT, wx.ALIGN_CENTER) |
|
1417 else: |
|
1418 self.SetCellAlignment(row, col, wx.ALIGN_CENTRE, wx.ALIGN_CENTER) |
|
1419 |
|
1420 self.SetReadOnly(row, col, True) |
|
1421 col = col + 1 |
|
1422 row = row + 1 |
|
1423 |
|
1424 |
|
1425 #------------------------------------------------------------------------------- |
|
1426 # For Register Access Panel |
|
1427 #------------------------------------------------------------------------------- |
|
1428 class RegisterAccessPanel(wx.Panel): |
|
1429 def __init__(self, parent, controler): |
|
1430 """ |
|
1431 Constructor |
|
1432 @param parent: EEPROMAccessPanel object |
|
1433 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
1434 """ |
|
1435 self.parent = parent |
|
1436 self.Controler = controler |
|
1437 self.__init_data() |
|
1438 |
|
1439 wx.Panel.__init__(self, parent, -1) |
|
1440 |
|
1441 sizer = wx.FlexGridSizer(cols=1, hgap=20, rows=2, vgap=5) |
|
1442 button_sizer = wx.FlexGridSizer(cols=2, hgap=10, rows=1, vgap=10) |
|
1443 |
|
1444 self.ReloadButton = wx.Button(self, -1, "Reload") |
|
1445 self.CompactViewCheckbox = wx.CheckBox(self, -1, "Compact View") |
|
1446 self.RegisterNotebook = RegisterNotebook(self, self.Controler) |
|
1447 |
|
1448 button_sizer.AddMany([self.ReloadButton, self.CompactViewCheckbox]) |
|
1449 sizer.AddMany([button_sizer, self.RegisterNotebook]) |
|
1450 self.SetSizer(sizer) |
|
1451 |
|
1452 self.ReloadButton.Bind(wx.EVT_BUTTON, self.OnReloadButton) |
|
1453 self.CompactViewCheckbox.Bind(wx.EVT_CHECKBOX, self.ToggleCompactViewCheckbox) |
|
1454 |
|
1455 for index in range(4): |
|
1456 self.RegisterNotebook.RegPage[index].MainTable.CreateGrid(self.MainRow[index], self.MainCol) |
|
1457 self.RegisterNotebook.RegPage[index].MainTable.SetValue(self, 0, index*512, (index+1)*512) |
|
1458 |
|
1459 # data default setting |
|
1460 if self.Controler.CommonMethod.RegData == "": |
|
1461 self.CompactViewCheckbox.Disable() |
|
1462 for index in range(4): |
|
1463 self.RegisterNotebook.RegPage[index].MainTable.SetValue(self, 0, index*512, (index+1)*512) |
|
1464 else: # If data was saved, |
|
1465 self.BasicSetData() |
|
1466 self.ParseData() |
|
1467 for index in range(4): |
|
1468 self.RegisterNotebook.RegPage[index].MainTable.SetValue(self, self.RegMonitorData, index*512, (index+1)*512) |
|
1469 |
|
1470 def __init_data(self): |
|
1471 """ |
|
1472 Declare initial data. |
|
1473 """ |
|
1474 # flag for compact view |
|
1475 self.CompactFlag = False |
|
1476 |
|
1477 # main grid의 rows and cols |
|
1478 self.MainRow = [512, 512, 512, 512] |
|
1479 self.MainCol = 4 |
|
1480 |
|
1481 # main grids' data range |
|
1482 self.PageRange = [] |
|
1483 for index in range(4): |
|
1484 self.PageRange.append([512*index, 512*(index+1)]) |
|
1485 |
|
1486 # Previous value of register data for register description configuration |
|
1487 self.PreRegSpec = {"ESCType": "", |
|
1488 "FMMUNumber": "", |
|
1489 "SMNumber": "", |
|
1490 "PDIType": ""} |
|
1491 |
|
1492 def LoadData(self): |
|
1493 """ |
|
1494 Get data from the register. |
|
1495 """ |
|
1496 self.Controler.CommonMethod.RegData = "" |
|
1497 #ethercat reg_read |
|
1498 #ex : ethercat reg_read -p 0 0x0000 0x0001 |
|
1499 #return value : 0x11 |
|
1500 for index in range(4): |
|
1501 self.Controler.CommonMethod.RegData = self.Controler.CommonMethod.RegData + " " + self.Controler.CommonMethod.RegRead("0x"+"{:0>4x}".format(index*1024), "0x0400") |
|
1502 |
|
1503 # store previous value |
|
1504 # (ESC type, port number of FMMU, port number of SM, and PDI type)) |
|
1505 for reg_spec in ["ESCType","FMMUNumber","SMNumber", "PDIType"]: |
|
1506 self.PreRegSpec[reg_spec] = self.Controler.CommonMethod.CrtRegSpec[reg_spec] |
|
1507 |
|
1508 # update registers' description |
|
1509 # (ESC type, port number of FMMU, port number of SM, and PDI type) |
|
1510 for reg_spec, address in [("ESCType", "0x0000"), |
|
1511 ("FMMUNumber", "0x0004"), |
|
1512 ("SMNumber", "0x0005"), |
|
1513 ("PDIType", "0x0140")]: |
|
1514 self.Controler.CommonMethod.CrtRegSpec[reg_spec] = self.Controler.CommonMethod.RegRead(address, "0x0001") |
|
1515 |
|
1516 # Enable compactView checkbox |
|
1517 self.CompactViewCheckbox.Enable() |
|
1518 |
|
1519 def BasicSetData(self): |
|
1520 """ |
|
1521 Get and save the description of registers. |
|
1522 It's done by parsing register_information.xml. |
|
1523 """ |
|
1524 # parse the above register's value |
|
1525 # If the value is 0x12, the result is 12 |
|
1526 self.ESCType = self.Controler.CommonMethod.CrtRegSpec["ESCType"].split('x')[1] |
|
1527 self.PDIType = self.Controler.CommonMethod.CrtRegSpec["PDIType"].split('x')[1] |
|
1528 # If the value is 0x12, the result is 18 (It's converted to decimal value) |
|
1529 self.FMMUNumber = int(self.Controler.CommonMethod.CrtRegSpec["FMMUNumber"], 16) |
|
1530 self.SMNumber = int(self.Controler.CommonMethod.CrtRegSpec["SMNumber"], 16) |
|
1531 |
|
1532 # initialize description dictionary of register main table and register sub table. |
|
1533 self.RegisterDescriptionDict = {} |
|
1534 self.RegisterSubGridDict = {} |
|
1535 |
|
1536 # ./EthercatMaster/register_information.xml contains register description. |
|
1537 if wx.Platform == '__WXMSW__': |
|
1538 reg_info_file = open("../../EthercatMaster/register_information.xml", 'r') |
|
1539 else: |
|
1540 reg_info_file = open("./EthercatMaster/register_information.xml", 'r') |
|
1541 reg_info_tree = minidom.parse(reg_info_file) |
|
1542 reg_info_file.close() |
|
1543 |
|
1544 # parse register description |
|
1545 for register_info in reg_info_tree.childNodes: |
|
1546 for register in register_info.childNodes: |
|
1547 if register.nodeType == reg_info_tree.ELEMENT_NODE and register.nodeName == "Register": |
|
1548 # If it depends on the property(ESC type, PDI type, FMMU number, SM number) |
|
1549 for property, type, value in [("esc", "type", self.ESCType), |
|
1550 ("pdi", "type", self.PDIType), |
|
1551 ("fmmu", "number", self.FMMUNumber), |
|
1552 ("sm", "number", self.SMNumber)]: |
|
1553 if property in register.attributes.keys(): |
|
1554 if type == "type": |
|
1555 if register.attributes[property].value == value: |
|
1556 self.GetRegisterInfo(reg_info_tree, register) |
|
1557 break |
|
1558 else: # type == "number" |
|
1559 if register.attributes[property].value < value: |
|
1560 self.GetRegisterInfo(reg_info_tree, register) |
|
1561 break |
|
1562 else: |
|
1563 self.GetRegisterInfo(reg_info_tree, register) |
|
1564 break |
|
1565 |
|
1566 def GetRegisterInfo(self, reg_info_tree, register): |
|
1567 """ |
|
1568 Save the register's description into the dictionary. |
|
1569 reg_info_tree is based on the register_information.xml. |
|
1570 @param reg_info_tree: XML tree |
|
1571 @param register: register which you want to get the description |
|
1572 """ |
|
1573 # temporary variables for register main table idescription dictionary |
|
1574 reg_index = "" |
|
1575 reg_main_description = "" |
|
1576 |
|
1577 for data in register.childNodes: |
|
1578 if data.nodeType == reg_info_tree.ELEMENT_NODE and data.nodeName == "Index": |
|
1579 for index in data.childNodes: |
|
1580 reg_index = index.nodeValue |
|
1581 if data.nodeType == reg_info_tree.ELEMENT_NODE and data.nodeName == "Description": |
|
1582 for description in data.childNodes: |
|
1583 reg_main_description = description.nodeValue |
|
1584 |
|
1585 # Add description for register main table |
|
1586 if reg_index is not "" and reg_main_description is not "": |
|
1587 self.RegisterDescriptionDict[reg_index] = reg_main_description |
|
1588 |
|
1589 if data.nodeType == reg_info_tree.ELEMENT_NODE and data.nodeName == "Details": |
|
1590 # declare register sub table description dictionary about this index |
|
1591 self.RegisterSubGridDict[reg_index] = [] |
|
1592 |
|
1593 for detail in data.childNodes: |
|
1594 if detail.nodeType == reg_info_tree.ELEMENT_NODE and detail.nodeName == "Detail": |
|
1595 # If it depends on the property(ESC type, PDI type, FMMU number, SM number) |
|
1596 for property, type, value in [("esc", "type", self.ESCType), |
|
1597 ("pdi", "type", self.PDIType), |
|
1598 ("fmmu", "number", self.FMMUNumber), |
|
1599 ("sm", "number", self.SMNumber)]: |
|
1600 if property in detail.attributes.keys(): |
|
1601 if type == "type": |
|
1602 if detail.attributes[property].value == value: |
|
1603 self.GetRegisterDetailInfo(reg_info_tree, reg_index, detail) |
|
1604 break |
|
1605 else: # type == "number" |
|
1606 if detail.attributes[property].value < value: |
|
1607 self.GetRegisterDetailInfo(reg_info_tree, reg_index, detail) |
|
1608 break |
|
1609 else: |
|
1610 self.GetRegisterDetailInfo(reg_info_tree, reg_index, detail) |
|
1611 break |
|
1612 |
|
1613 def GetRegisterDetailInfo(self, reg_info_tree, reg_index, detail): |
|
1614 """ |
|
1615 Get the resgister's detailed description(for sub table) from the reg_info_tree. |
|
1616 @param reg_info_tree: XML tree (register_information.xml) |
|
1617 @param reg_index: index of the register |
|
1618 @param detail: description of the register |
|
1619 """ |
|
1620 # temporary variables for register sub table description dictionary |
|
1621 # - It is initialized in every sub description |
|
1622 reg_bit_range = "" |
|
1623 reg_sub_description = "" |
|
1624 reg_enum_dictionary = {} |
|
1625 |
|
1626 for detail_data in detail.childNodes: |
|
1627 if detail_data.nodeType == reg_info_tree.ELEMENT_NODE and detail_data.nodeName == "Range": |
|
1628 for range in detail_data.childNodes: |
|
1629 reg_bit_range = range.nodeValue |
|
1630 if detail_data.nodeType == reg_info_tree.ELEMENT_NODE and detail_data.nodeName == "Description": |
|
1631 for description in detail_data.childNodes: |
|
1632 reg_sub_description = description.nodeValue |
|
1633 |
|
1634 if detail_data.nodeType == reg_info_tree.ELEMENT_NODE and detail_data.nodeName == "Enum": |
|
1635 for enum in detail_data.childNodes: |
|
1636 if enum.nodeType == reg_info_tree.ELEMENT_NODE and enum.nodeName == "item": |
|
1637 |
|
1638 # temporary variables for a description of each value |
|
1639 # For example, if the bit is 1, it is 'enabled'('On', 'True', etc.), |
|
1640 # otherwise 'disabled'('Off', 'False', etc.). |
|
1641 reg_sub_value = "" |
|
1642 reg_sub_value_description = "" |
|
1643 |
|
1644 for item in enum.childNodes: |
|
1645 if item.nodeType == reg_info_tree.ELEMENT_NODE and item.nodeName == "value": |
|
1646 for value in item.childNodes: |
|
1647 reg_sub_value = value.nodeValue |
|
1648 if item.nodeType == reg_info_tree.ELEMENT_NODE and item.nodeName == "Description": |
|
1649 for description in item.childNodes: |
|
1650 reg_sub_value_description = description.nodeValue |
|
1651 |
|
1652 # Add a description of each value to register enum dictionary |
|
1653 if reg_sub_value is not "" and reg_sub_value_description is not "": |
|
1654 reg_enum_dictionary[reg_sub_value] = reg_sub_value_description |
|
1655 |
|
1656 # add a description to register sub table description dictionary |
|
1657 if reg_bit_range is not "" and reg_sub_description is not "": |
|
1658 self.RegisterSubGridDict[reg_index].append([reg_bit_range, |
|
1659 reg_sub_description, reg_enum_dictionary]) |
|
1660 |
|
1661 def ParseData(self): |
|
1662 """ |
|
1663 Transform the data into dec, hex, string, and description |
|
1664 """ |
|
1665 row_data = [] |
|
1666 self.RegMonitorData = [] |
|
1667 reg_word = "" |
|
1668 |
|
1669 reg_data = self.Controler.CommonMethod.RegData.split() |
|
1670 |
|
1671 # loop for register(0x0000:0x0fff) |
|
1672 for address in range(0x1000): |
|
1673 # arrange 2 Bytes of register data |
|
1674 reg_word = reg_data[address].split('x')[1] + reg_word |
|
1675 if (address%2) == 1: |
|
1676 # append address |
|
1677 hex_address = "{:0>4x}".format(address-1) |
|
1678 row_data.append(hex_address) |
|
1679 |
|
1680 # append description |
|
1681 if self.RegisterDescriptionDict.has_key(hex_address): |
|
1682 row_data.append(self.RegisterDescriptionDict[hex_address]) |
|
1683 else: |
|
1684 row_data.append("") |
|
1685 |
|
1686 # append Decimal value |
|
1687 row_data.append(str(int(reg_word, 16))) |
|
1688 |
|
1689 # append Hex value |
|
1690 row_data.append('0x'+reg_word) |
|
1691 |
|
1692 # append ASCII value |
|
1693 char_data = "" |
|
1694 for iter in range(2): |
|
1695 if int(reg_word[iter*2:iter*2+2], 16)>=32 and int(reg_word[iter*2:iter*2+2], 16)<=126: |
|
1696 char_data = char_data + chr(int(reg_word[iter*2:iter*2+2], 16)) |
|
1697 else: |
|
1698 char_data = char_data + "." |
|
1699 row_data.append(char_data) |
|
1700 |
|
1701 self.RegMonitorData.append(row_data) |
|
1702 reg_word = "" # initialize regWord |
|
1703 row_data = [] |
|
1704 |
|
1705 def OnReloadButton(self, event): |
|
1706 """ |
|
1707 Handle the click event of the 'Reload' button. |
|
1708 Get the data from registers again, and update the table. |
|
1709 @param event: wx.EVT_BUTTON object |
|
1710 """ |
|
1711 # Check whether beremiz connected or not. |
|
1712 check_connect_flag = self.Controler.CommonMethod.CheckConnect(False) |
|
1713 if check_connect_flag: |
|
1714 self.LoadData() |
|
1715 self.BasicSetData() |
|
1716 self.ParseData() |
|
1717 # set data into UI |
|
1718 if self.CompactFlag: |
|
1719 self.ToggleCompactViewCheckbox(True) |
|
1720 else : |
|
1721 for index in range(4): |
|
1722 self.RegisterNotebook.RegPage[index].UpdateMainTable(self.MainRow[index], self.MainCol, |
|
1723 self.PageRange[index][0], self.PageRange[index][1], |
|
1724 self.RegMonitorData) |
|
1725 |
|
1726 def ToggleCompactViewCheckbox(self, event): |
|
1727 """ |
|
1728 Handles the event of the 'Compact view' check box. |
|
1729 If it's checked, show only the registers that have a description. |
|
1730 If not, show all the registers. |
|
1731 @param event: wx.EVT_CHECKBOX object |
|
1732 """ |
|
1733 |
|
1734 # If "Compact View" Checkbox is True |
|
1735 ## 'event' is argument of this method or event of checkbox. |
|
1736 if event==True or event.GetEventObject().GetValue(): |
|
1737 self.CompactFlag = True |
|
1738 |
|
1739 reg_compact_data = [] |
|
1740 page_row = [0, 0, 0, 0] |
|
1741 for index in range(4): |
|
1742 self.PageRange[index] = [0, 0] |
|
1743 |
|
1744 for reg_row_data in self.RegMonitorData: |
|
1745 if reg_row_data[1] is not "": |
|
1746 # data structure for "compact view" |
|
1747 reg_compact_data.append(reg_row_data) |
|
1748 # count for each register notebooks' row |
|
1749 # It compare with register's address. |
|
1750 for index in range(4): |
|
1751 if int('0x'+reg_row_data[0], 16) < (index+1)*1024: |
|
1752 page_row[index] += 1 |
|
1753 break |
|
1754 |
|
1755 # Setting tables' rows and cols, range for compact view |
|
1756 for index in range(4): |
|
1757 self.MainRow[index] = page_row[index] |
|
1758 self.PageRange[index][1] = page_row[index] |
|
1759 for iter in range(index): |
|
1760 self.PageRange[index][0] += page_row[iter] |
|
1761 self.PageRange[index][1] += page_row[iter] |
|
1762 |
|
1763 # Update table |
|
1764 for index in range(4): |
|
1765 self.RegisterNotebook.RegPage[index].UpdateMainTable(self.MainRow[index], self.MainCol, |
|
1766 self.PageRange[index][0], self.PageRange[index][1], |
|
1767 reg_compact_data) |
|
1768 |
|
1769 # Compact View Checkbox is False |
|
1770 else: |
|
1771 self.CompactFlag = False |
|
1772 # Setting original rows, cols and range |
|
1773 self.MainRow = [512, 512, 512, 512] |
|
1774 self.PageRange = [] |
|
1775 |
|
1776 for index in range(4): |
|
1777 self.PageRange.append([512*index, 512*(index+1)]) |
|
1778 |
|
1779 # Update table |
|
1780 for index in range(4): |
|
1781 self.RegisterNotebook.RegPage[index].UpdateMainTable(self.MainRow[index], self.MainCol, |
|
1782 self.PageRange[index][0], self.PageRange[index][1], |
|
1783 self.RegMonitorData) |
|
1784 |
|
1785 |
|
1786 #------------------------------------------------------------------------------- |
|
1787 # For Register Access Notebook (divide index range) |
|
1788 #------------------------------------------------------------------------------- |
|
1789 class RegisterNotebook(wx.Notebook): |
|
1790 def __init__(self, parent, controler): |
|
1791 """ |
|
1792 Constructor |
|
1793 @param parent: RegisterAccessPanel object |
|
1794 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
1795 """ |
|
1796 wx.Notebook.__init__(self, parent, id = -1) |
|
1797 |
|
1798 self.parent = parent |
|
1799 self.Controler = controler |
|
1800 |
|
1801 # Initialize pages |
|
1802 self.RegPage = [] |
|
1803 for iter in range(4): |
|
1804 self.RegPage.append(None) |
|
1805 |
|
1806 for index in range(4): |
|
1807 self.RegPage[index] = RegisterNotebookPanel(self, self.Controler, |
|
1808 parent.MainRow[index], parent.MainCol) |
|
1809 self.AddPage(self.RegPage[index], |
|
1810 "0x"+"{:0>4x}".format(index*1024)+" - 0x"+"{:0>4x}".format((index+1)*1024-1)) |
|
1811 |
|
1812 self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged) |
|
1813 self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnPageChanging) |
|
1814 |
|
1815 def OnPageChanged(self, event): |
|
1816 old = event.GetOldSelection() |
|
1817 new = event.GetSelection() |
|
1818 sel = self.GetSelection() |
|
1819 event.Skip() |
|
1820 |
|
1821 def OnPageChanging(self, event): |
|
1822 old = event.GetOldSelection() |
|
1823 new = event.GetSelection() |
|
1824 sel = self.GetSelection() |
|
1825 event.Skip() |
|
1826 |
|
1827 |
|
1828 #------------------------------------------------------------------------------- |
|
1829 # For Register Access Notebook Panel |
|
1830 # (Main UI : including main, sub table) |
|
1831 #------------------------------------------------------------------------------- |
|
1832 class RegisterNotebookPanel(wx.Panel): |
|
1833 def __init__(self, parent, controler, row, col): |
|
1834 """ |
|
1835 Constructor |
|
1836 @param parent: RegisterAccessPanel object |
|
1837 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
1838 @param row, col: size of the table |
|
1839 """ |
|
1840 wx.Panel.__init__(self, parent, -1) |
|
1841 |
|
1842 self.parent = parent |
|
1843 self.Controler = controler |
|
1844 self.Row = row |
|
1845 self.Col = col |
|
1846 sub_row = 0 |
|
1847 sub_col = 4 |
|
1848 |
|
1849 self.Sizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=30) |
|
1850 |
|
1851 self.MainTable = RegisterMainTable(self, self.Row, self.Col, self.Controler) |
|
1852 self.SubTable = RegisterSubTable(self, sub_row, sub_col) |
|
1853 |
|
1854 self.SubTable.CreateGrid(sub_row, sub_col) |
|
1855 self.SubTable.SetValue(self, []) |
|
1856 |
|
1857 self.Sizer.AddMany([self.MainTable, self.SubTable]) |
|
1858 |
|
1859 self.SetSizer(self.Sizer) |
|
1860 |
|
1861 def UpdateMainTable(self, row, col, low_index, high_index, data): |
|
1862 """ |
|
1863 Updates main table. |
|
1864 It's done by deleting the main table and creating it again. |
|
1865 @param row, col: size of the table |
|
1866 @param low_index: the lowest index of the page |
|
1867 @param high_index: the highest index of the page |
|
1868 @param data: data |
|
1869 """ |
|
1870 self.MainTable.Destroy() |
|
1871 self.MainTable = RegisterMainTable(self, row, col, self.Controler) |
|
1872 self.Sizer.Detach(self.SubTable) |
|
1873 self.Sizer.AddMany([self.MainTable, self.SubTable]) |
|
1874 self.SetSizer(self.Sizer) |
|
1875 self.MainTable.CreateGrid(row, col) |
|
1876 self.MainTable.SetValue(self, data, low_index, high_index) |
|
1877 self.MainTable.Update() |
|
1878 |
|
1879 def UpdateSubTable(self, row, col, data): |
|
1880 """ |
|
1881 Updates sub table. |
|
1882 It's done by deleting the sub table and creating it again. |
|
1883 @param row, col: size of the table |
|
1884 @param data: data |
|
1885 """ |
|
1886 self.SubTable.Destroy() |
|
1887 self.SubTable = RegisterSubTable(self, row, col) |
|
1888 self.Sizer.Detach(self.MainTable) |
|
1889 self.Sizer.AddMany([self.MainTable, self.SubTable]) |
|
1890 self.Sizer.Layout() |
|
1891 self.SetSizer(self.Sizer) |
|
1892 self.SubTable.CreateGrid(row, col) |
|
1893 self.SubTable.SetValue(self, data) |
|
1894 self.SubTable.Update() |
|
1895 |
|
1896 |
|
1897 #------------------------------------------------------------------------------- |
|
1898 # For Register Access Notebook Panel (Main Table) |
|
1899 #------------------------------------------------------------------------------- |
|
1900 class RegisterMainTable(wx.grid.Grid): |
|
1901 def __init__(self, parent, row, col, controler): |
|
1902 """ |
|
1903 Constructor |
|
1904 @param parent: RegisterNotebook object |
|
1905 @param row, col: size of the table |
|
1906 @param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
1907 """ |
|
1908 self.parent = parent |
|
1909 self.Data = {} |
|
1910 self.Row = row |
|
1911 self.Col = col |
|
1912 self.Controler = controler |
|
1913 self.RegisterAccessPanel = self.parent.parent.parent |
|
1914 |
|
1915 wx.grid.Grid.__init__(self, parent, -1, size=(820,300), |
|
1916 style=wx.EXPAND|wx.ALIGN_CENTRE_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) |
|
1917 |
|
1918 for evt, mapping_method in [(gridlib.EVT_GRID_CELL_LEFT_CLICK, self.OnSelectCell), |
|
1919 (gridlib.EVT_GRID_CELL_LEFT_CLICK, self.OnSelectCell), |
|
1920 (gridlib.EVT_GRID_CELL_LEFT_DCLICK, self.OnRegModifyDialog)]: |
|
1921 self.Bind(evt, mapping_method) |
|
1922 |
|
1923 def SetValue(self, parent, reg_monitor_data, low_index, high_index): |
|
1924 """ |
|
1925 Set the RegMonitorData into the main table. |
|
1926 @param parent: RegisterNotebook object |
|
1927 @param reg_monitor_data: data |
|
1928 @param low_index: the lowest index of the page |
|
1929 @param high_index: the highest index of the page |
|
1930 """ |
|
1931 self.RegMonitorData = reg_monitor_data |
|
1932 |
|
1933 # set label name and size |
|
1934 register_maintable_label = [(0, "Description"), (1, "Dec"), |
|
1935 (2, "Hex"), (3, "Char")] |
|
1936 |
|
1937 for (index, label) in register_maintable_label: |
|
1938 self.SetColLabelValue(index, label) |
|
1939 |
|
1940 self.SetColSize(0, 200) |
|
1941 |
|
1942 # if reg_monitor_data is 0, it is initialization of register access. |
|
1943 if reg_monitor_data == 0: |
|
1944 return 0 |
|
1945 |
|
1946 # set data into UI |
|
1947 row = col = 0 |
|
1948 for row_index in reg_monitor_data[low_index:high_index]: |
|
1949 col = 0 |
|
1950 self.SetRowLabelValue(row, row_index[0]) |
|
1951 for data_index in range(4): |
|
1952 self.SetCellValue(row, col, row_index[data_index+1]) |
|
1953 self.SetCellAlignment(row, col, wx.ALIGN_CENTRE, wx.ALIGN_CENTER) |
|
1954 self.SetReadOnly(row, col, True) |
|
1955 col = col + 1 |
|
1956 row = row + 1 |
|
1957 |
|
1958 def OnSelectCell(self, event): |
|
1959 """ |
|
1960 Handles the event of the cell of the main table. |
|
1961 @param event: gridlib object (left click) |
|
1962 """ |
|
1963 # if reg_monitor_data is 0, it is initialization of register access. |
|
1964 if self.RegMonitorData == 0: |
|
1965 event.Skip() |
|
1966 return 0 |
|
1967 |
|
1968 sub_row = 0 |
|
1969 sub_col = 4 |
|
1970 |
|
1971 address = self.GetRowLabelValue(event.GetRow()) |
|
1972 |
|
1973 reg_sub_grid_data = [] |
|
1974 |
|
1975 BIT_RANGE, NAME, DESCRIPTIONS = range(3) |
|
1976 |
|
1977 # Check if this register's detail description is exist or not, |
|
1978 # and create data structure for the detail description table ; sub grid |
|
1979 if address in self.RegisterAccessPanel.RegisterSubGridDict: |
|
1980 for element in self.RegisterAccessPanel.RegisterSubGridDict[address]: |
|
1981 row_data =[] |
|
1982 row_data.append(element[BIT_RANGE]) |
|
1983 row_data.append(element[NAME]) |
|
1984 bin_data = "{:0>16b}".format(int(self.GetCellValue(event.GetRow(), 1))) |
|
1985 value_range = element[BIT_RANGE].split('-') |
|
1986 value = (bin_data[8:16][::-1]+bin_data[0:8][::-1])[int(value_range[0]):(int(value_range[-1])+1)][::-1] |
|
1987 row_data.append(str(int(('0b'+str(value)), 2))) |
|
1988 if value in element[DESCRIPTIONS]: |
|
1989 row_data.append(element[DESCRIPTIONS][value]) |
|
1990 else: |
|
1991 row_data.append('') |
|
1992 reg_sub_grid_data.append(row_data) |
|
1993 sub_row = sub_row + 1 |
|
1994 |
|
1995 self.parent.UpdateSubTable(sub_row, sub_col, reg_sub_grid_data) |
|
1996 # event.Skip() updates UI of selecting cell |
|
1997 event.Skip() |
|
1998 |
|
1999 def OnRegModifyDialog(self, event): |
|
2000 """ |
|
2001 Handle the event of the cell of the main table. |
|
2002 Display the window where the user modifies the value of the cell. |
|
2003 @param event: gridlib object (double click) |
|
2004 """ |
|
2005 # user can enter a value in case that user double-clicked 'Dec' or 'Hex' value. |
|
2006 if event.GetCol() == 1 or event.GetCol() == 2: |
|
2007 dlg = wx.TextEntryDialog(self, "Enter hex(0xnnnn) or dec(n) value", |
|
2008 "Register Modify Dialog", style = wx.OK|wx.CANCEL) |
|
2009 |
|
2010 # Setting value in initial dialog value |
|
2011 start_value = self.GetCellValue(event.GetRow(), event.GetCol()) |
|
2012 dlg.SetValue(start_value) |
|
2013 |
|
2014 if dlg.ShowModal() == wx.ID_OK: |
|
2015 try: |
|
2016 # It int(input) success, this input is dev or hex value. |
|
2017 # Otherwise, it's error, so it goes except. |
|
2018 int(dlg.GetValue(), 0) |
|
2019 |
|
2020 # reg_write |
|
2021 # ex) ethercat reg_write -p 0 -t uint16 0x0000 0x0000 |
|
2022 return_val = self.Controler.CommonMethod.RegWrite('0x'+self.GetRowLabelValue(event.GetRow()), dlg.GetValue()) |
|
2023 |
|
2024 if len(return_val)==0: |
|
2025 # set dec |
|
2026 self.SetCellValue(event.GetRow(), 1, str(int(dlg.GetValue(), 0))) |
|
2027 # set hex |
|
2028 hex_data = '0x'+"{:0>4x}".format(int(dlg.GetValue(), 0)) |
|
2029 self.SetCellValue(event.GetRow(), 2, hex_data) |
|
2030 # set char |
|
2031 char_data = "" |
|
2032 # If hex_data is been able to convert to ascii code, append ascii code. |
|
2033 for iter in range(2): |
|
2034 if int(hex_data[(iter+1)*2:(iter+2)*2], 16)>=32 and int(hex_data[(iter+1)*2:(iter+2)*2], 16)<=126: |
|
2035 char_data = char_data + chr(int(hex_data[(iter+1)*2:(iter+2)*2], 16)) |
|
2036 else: |
|
2037 char_data = char_data + "." |
|
2038 |
|
2039 self.SetCellValue(event.GetRow(), 3, char_data) |
|
2040 |
|
2041 else: |
|
2042 self.Controler.CommonMethod.CreateErrorDialog('You can\'t modify it. This register is read-only or it\'s not connected.') |
|
2043 |
|
2044 except ValueError: |
|
2045 self.Controler.CommonMethod.CreateErrorDialog('You entered wrong value. You can enter dec or hex value only.') |
|
2046 |
|
2047 |
|
2048 #------------------------------------------------------------------------------- |
|
2049 # For Register Access Notebook Panel (Sub Table) |
|
2050 #------------------------------------------------------------------------------- |
|
2051 class RegisterSubTable(wx.grid.Grid): |
|
2052 def __init__(self, parent, row, col): |
|
2053 """ |
|
2054 Constructor |
|
2055 @param parent: RegisterNotebook object |
|
2056 @param row, col: size of the table |
|
2057 """ |
|
2058 self.parent = parent |
|
2059 self.Data = {} |
|
2060 self.Row = row |
|
2061 self.Col = col |
|
2062 |
|
2063 wx.grid.Grid.__init__(self, parent, -1, size=(820,150), |
|
2064 style=wx.EXPAND|wx.ALIGN_CENTRE_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) |
|
2065 |
|
2066 def SetValue(self, parent, data): |
|
2067 """ |
|
2068 Set the data into the subtable. |
|
2069 @param parent: RegisterNotebook object |
|
2070 @param data: data |
|
2071 """ |
|
2072 # lset label name and size |
|
2073 Register_SubTable_Label = [(0, "Bits"), (1, "Name"), |
|
2074 (2, "Value"), (3, "Enum")] |
|
2075 |
|
2076 for (index, label) in Register_SubTable_Label: |
|
2077 self.SetColLabelValue(index, label) |
|
2078 |
|
2079 self.SetColSize(1, 200) |
|
2080 self.SetColSize(3, 200) |
|
2081 |
|
2082 # set data into table |
|
2083 row = col = 0 |
|
2084 for rowData in data: |
|
2085 col = 0 |
|
2086 for element in rowData: |
|
2087 self.SetCellValue(row, col, element) |
|
2088 self.SetCellAlignment(row, col, wx.ALIGN_CENTRE, wx.ALIGN_CENTER) |
|
2089 self.SetReadOnly(row, col, True) |
|
2090 col = col + 1 |
|
2091 row = row + 1 |
|
2092 |
|
2093 |
|
2094 #------------------------------------------------------------------------------- |
|
2095 # For Master State Panel |
|
2096 #------------------------------------------------------------------------------- |
|
2097 class MasterStatePanelClass(wx.Panel): |
|
2098 def __init__(self, parent, controler): |
|
2099 """ |
|
2100 Constructor |
|
2101 @param parent: wx.ScrollWindow object |
|
2102 @Param controler: _EthercatSlaveCTN class in EthercatSlave.py |
|
2103 """ |
|
2104 wx.Panel.__init__(self, parent, -1, (0, 0), |
|
2105 size=wx.DefaultSize, style = wx.SUNKEN_BORDER) |
|
2106 self.Controler = controler |
|
2107 self.parent = parent |
|
2108 self.StaticBox = {} |
|
2109 self.StaticText = {} |
|
2110 self.TextCtrl = {} |
|
2111 |
|
2112 # ----------------------- Main Sizer and Update Button -------------------------------------------- |
|
2113 self.MasterStateSizer = {"main" : wx.BoxSizer(wx.VERTICAL)} |
|
2114 for key, attr in [ |
|
2115 ("innerMain", [1, 10, 2, 10]), |
|
2116 ("innerTopHalf", [2, 10, 1, 10]), |
|
2117 ("innerBottomHalf", [2, 10, 1, 10]), |
|
2118 ("innerMasterState", [2, 10, 3, 10]), |
|
2119 ("innerDeviceInfo", [4, 10, 3, 10]), |
|
2120 ("innerFrameInfo", [4, 10, 5, 10])]: |
|
2121 self.MasterStateSizer[key] = wx.FlexGridSizer(cols=attr[0], hgap=attr[1], rows=attr[2], vgap=attr[3]) |
|
2122 |
|
2123 |
|
2124 self.UpdateButton = wx.Button(self, label=_('Update')) |
|
2125 self.UpdateButton.Bind(wx.EVT_BUTTON, self.OnButtonClick) |
|
2126 |
|
2127 for key, label in [ |
|
2128 ('masterState', 'EtherCAT Master State'), |
|
2129 ('deviceInfo', 'Ethernet Network Card Information'), |
|
2130 ('frameInfo', 'Network Frame Information')]: |
|
2131 self.StaticBox[key] = wx.StaticBox(self, label=_(label)) |
|
2132 self.MasterStateSizer[key] = wx.StaticBoxSizer(self.StaticBox[key]) |
|
2133 |
|
2134 |
|
2135 # ----------------------- Master State ----------------------------------------------------------- |
|
2136 for key, label in [ |
|
2137 ('Phase', 'Phase:'), |
|
2138 ('Active', 'Active:'), |
|
2139 ('Slaves', 'Slave Count:')]: |
|
2140 self.StaticText[key] = wx.StaticText(self, label=_(label)) |
|
2141 self.TextCtrl[key] = wx.TextCtrl(self, size=wx.Size(130, 24), style=wx.TE_READONLY) |
|
2142 self.MasterStateSizer['innerMasterState'].AddMany([self.StaticText[key], self.TextCtrl[key]]) |
|
2143 |
|
2144 self.MasterStateSizer['masterState'].AddSizer(self.MasterStateSizer['innerMasterState']) |
|
2145 |
|
2146 # ----------------------- Ethernet Network Card Information --------------------------------------- |
|
2147 for key, label in [ |
|
2148 ('Main', 'MAC Address:'), |
|
2149 ('Link', 'Link State:'), |
|
2150 ('Tx frames', 'Tx Frames:'), |
|
2151 ('Rx frames', 'Rx Frames:'), |
|
2152 ('Lost frames', 'Lost Frames:')]: |
|
2153 self.StaticText[key] = wx.StaticText(self, label=_(label)) |
|
2154 self.TextCtrl[key] = wx.TextCtrl(self, size=wx.Size(130, 24), style=wx.TE_READONLY) |
|
2155 self.MasterStateSizer['innerDeviceInfo'].AddMany([self.StaticText[key], self.TextCtrl[key]]) |
|
2156 |
|
2157 self.MasterStateSizer['deviceInfo'].AddSizer(self.MasterStateSizer['innerDeviceInfo']) |
|
2158 |
|
2159 # ----------------------- Network Frame Information ----------------------------------------------- |
|
2160 for key, label in [ |
|
2161 ('Tx frame rate [1/s]', 'Tx Frame Rate [1/s]:'), |
|
2162 ('Rx frame rate [1/s]', 'Tx Rate [kByte/s]:'), |
|
2163 ('Loss rate [1/s]', 'Loss Rate [1/s]:'), |
|
2164 ('Frame loss [%]', 'Frame Loss [%]:')]: |
|
2165 self.StaticText[key] = wx.StaticText(self, label=_(label)) |
|
2166 self.MasterStateSizer['innerFrameInfo'].Add(self.StaticText[key]) |
|
2167 self.TextCtrl[key] = {} |
|
2168 for index in ['0', '1', '2']: |
|
2169 self.TextCtrl[key][index] = wx.TextCtrl(self, size=wx.Size(130, 24), style=wx.TE_READONLY) |
|
2170 self.MasterStateSizer['innerFrameInfo'].Add(self.TextCtrl[key][index]) |
|
2171 |
|
2172 self.MasterStateSizer['frameInfo'].AddSizer(self.MasterStateSizer['innerFrameInfo']) |
|
2173 |
|
2174 # --------------------------------- Main Sizer ---------------------------------------------------- |
|
2175 for key, sub, in [ |
|
2176 ('innerTopHalf', [ |
|
2177 'masterState', 'deviceInfo']), |
|
2178 ('innerBottomHalf', [ |
|
2179 'frameInfo']), |
|
2180 ('innerMain', [ |
|
2181 'innerTopHalf', 'innerBottomHalf'])]: |
|
2182 for key2 in sub: |
|
2183 self.MasterStateSizer[key].AddSizer(self.MasterStateSizer[key2]) |
|
2184 |
|
2185 self.MasterStateSizer['main'].AddSizer(self.UpdateButton) |
|
2186 self.MasterStateSizer['main'].AddSizer(self.MasterStateSizer['innerMain']) |
|
2187 |
|
2188 self.SetSizer(self.MasterStateSizer['main']) |
|
2189 |
|
2190 def OnButtonClick(self, event): |
|
2191 """ |
|
2192 Handle the event of the 'Update' button. |
|
2193 Update the data of the master state. |
|
2194 @param event: wx.EVT_BUTTON object |
|
2195 """ |
|
2196 if self.Controler.GetCTRoot()._connector is not None: |
|
2197 self.MasterState = self.Controler.CommonMethod.GetMasterState() |
|
2198 # Update each TextCtrl |
|
2199 if self.MasterState: |
|
2200 for key in self.TextCtrl: |
|
2201 if isinstance(self.TextCtrl[key], dict): |
|
2202 for index in self.TextCtrl[key]: |
|
2203 self.TextCtrl[key][index].SetValue(self.MasterState[key][int(index)]) |
|
2204 else: |
|
2205 self.TextCtrl[key].SetValue(self.MasterState[key][0]) |
|
2206 else : |
|
2207 self.Controler.CommonMethod.CreateErrorDialog('PLC not connected!') |