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