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