class MainWin(wx.Frame): def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) dataDir = Utils.getHomeDir() configFileName = os.path.join(dataDir, 'CrossMgrImpinj.cfg') self.config = wx.Config(appName="CrossMgrImpinj", vendorName="SmartCyclingSolutions", localFilename=configFileName) ID_MENU_ADVANCECONFIG = wx.NewIdRef() ID_MENU_COPYLOGS = wx.NewIdRef() ID_MENU_AUTODETECT = wx.NewIdRef() self.menuBar = wx.MenuBar(wx.MB_DOCKABLE) if 'WXMAC' in wx.Platform: self.appleMenu = self.menuBar.OSXGetAppleMenu() self.appleMenu.SetTitle("CrossMgrImpinj") self.appleMenu.Insert(0, wx.ID_ABOUT, "&About") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.editMenu = wx.Menu() self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_ADVANCECONFIG, "A&dvanced Configuration")) self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_COPYLOGS, "&Copy Logs to Clipboard")) self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_AUTODETECT, "&Autodetect Reader")) self.Bind(wx.EVT_MENU, self.doAdvanced, id=ID_MENU_ADVANCECONFIG) self.Bind(wx.EVT_MENU, self.doCopyToClipboard, id=ID_MENU_COPYLOGS) self.Bind(wx.EVT_MENU, self.doAutoDetect, id=ID_MENU_AUTODETECT) self.menuBar.Append(self.editMenu, "&Edit") else: self.fileMenu = wx.Menu() self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_ADVANCECONFIG, "A&dvanced Configuration")) self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_COPYLOGS, "&Copy Logs to Clipboard")) self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_AUTODETECT, "&Autodetect Reader")) self.fileMenu.Append(wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.doAdvanced, id=ID_MENU_ADVANCECONFIG) self.Bind(wx.EVT_MENU, self.doCopyToClipboard, id=ID_MENU_COPYLOGS) self.Bind(wx.EVT_MENU, self.doAutoDetect, id=ID_MENU_AUTODETECT) self.Bind(wx.EVT_MENU, self.onCloseWindow, id=wx.ID_EXIT) self.menuBar.Append(self.fileMenu, "&File") self.helpMenu = wx.Menu() self.helpMenu.Insert(0, wx.ID_ABOUT, "&About") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.menuBar.Append(self.helpMenu, "&Help") self.SetMenuBar(self.menuBar) self.SetBackgroundColour(wx.Colour(232, 232, 232)) self.LightGreen = wx.Colour(153, 255, 153) self.LightRed = wx.Colour(255, 153, 153) font = self.GetFont() bigFont = wx.Font(int(font.GetPointSize() * 1.2), font.GetFamily(), font.GetStyle(), wx.FONTWEIGHT_BOLD) titleFont = wx.Font(int(bigFont.GetPointSize() * 2.2), bigFont.GetFamily(), bigFont.GetStyle(), bigFont.GetWeight()) self.vbs = wx.BoxSizer(wx.VERTICAL) bs = wx.BoxSizer(wx.HORIZONTAL) self.reset = RoundButton(self, label='Reset', size=(80, 80)) self.reset.SetBackgroundColour(wx.WHITE) self.reset.SetForegroundColour(wx.Colour(0, 128, 128)) self.reset.SetFontToFitLabel( ) # Use the button's default font, but change the font size to fit the label. self.reset.Bind(wx.EVT_BUTTON, self.doReset) self.reset.Refresh() bs.Add(self.reset, border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(titleFont, wx.StaticText(self, label='CrossMgrImpinj')), border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.AddStretchSpacer() bitmap = wx.Bitmap(clipboard_xpm) self.copyToClipboard = wx.BitmapButton(self, bitmap=bitmap) self.copyToClipboard.SetToolTip( wx.ToolTip('Copy Configuration and Logs to Clipboard...')) self.copyToClipboard.Bind(wx.EVT_BUTTON, self.doCopyToClipboard) bs.Add(self.copyToClipboard, border=32, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.tStart = datetime.datetime.now() bs.Add(setFont( bigFont, wx.StaticText(self, label='Last Reset: %s' % self.tStart.strftime('%H:%M:%S'))), border=10, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.runningTime = setFont(bigFont, wx.StaticText(self, label='00:00:00')) bs.Add(self.runningTime, border=20, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(bigFont, wx.StaticText(self, label=' / ')), flag=wx.ALIGN_CENTER_VERTICAL) self.time = setFont(bigFont, wx.StaticText(self, label='00:00:00')) bs.Add(self.time, flag=wx.ALIGN_CENTER_VERTICAL) self.vbs.Add(bs, flag=wx.ALL | wx.EXPAND, border=4) fgs = wx.FlexGridSizer(rows=2, cols=2, vgap=4, hgap=4) fgs.AddGrowableRow(1) fgs.AddGrowableCol(0) fgs.AddGrowableCol(1) fgs.SetFlexibleDirection(wx.BOTH) self.vbs.Add(fgs, flag=wx.EXPAND, proportion=5) #------------------------------------------------------------------------------------------------ # Impinj configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) iRow = 0 hb = wx.BoxSizer(wx.HORIZONTAL) hb.Add(setFont(bigFont, wx.StaticText(self, label='Impinj Configuration:')), flag=wx.ALIGN_CENTER_VERTICAL) self.autoDetectButton = wx.Button(self, label='Auto Detect') self.autoDetectButton.Bind(wx.EVT_BUTTON, self.doAutoDetect) hb.Add(self.autoDetectButton, flag=wx.LEFT, border=6) self.advancedButton = wx.Button(self, label='Advanced...') self.advancedButton.Bind(wx.EVT_BUTTON, self.doAdvanced) hb.Add(self.advancedButton, flag=wx.LEFT, border=6) gbs.Add(hb, pos=(iRow, 0), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gs = wx.GridSizer(rows=0, cols=4, vgap=0, hgap=2) self.antennaLabels = [] self.antennas = [] for i in range(4): self.antennaLabels.append( wx.StaticText(self, label='{}'.format(i + 1), style=wx.ALIGN_CENTER)) gs.Add(self.antennaLabels[-1], flag=wx.ALIGN_CENTER | wx.EXPAND) for i in range(4): cb = wx.CheckBox(self, wx.ID_ANY, '') if i < 2: cb.SetValue(True) cb.Bind(wx.EVT_CHECKBOX, lambda x: self.getAntennaStr()) gs.Add(cb, flag=wx.ALIGN_CENTER) self.antennas.append(cb) hb = wx.BoxSizer() hb.Add(gs) self.methodName = wx.StaticText(self) self.refreshMethodName() hb.Add(self.methodName, flag=wx.ALIGN_BOTTOM | wx.LEFT, border=8) gbs.Add(wx.StaticText(self, label='ANT Ports:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) iRow += 1 self.useHostName = wx.RadioButton(self, label='Host Name:', style=wx.RB_GROUP) gbs.Add(self.useHostName, pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) hb.Add(wx.StaticText(self, label=ImpinjHostNamePrefix), flag=wx.ALIGN_CENTER_VERTICAL) if 'WXMAC' in wx.Platform: self.impinjHostName = masked.TextCtrl( self, defaultValue='00-00-00', useFixedWidthFont=True, size=(80, -1), ) else: self.impinjHostName = masked.TextCtrl( self, mask='NN-NN-NN', defaultValue='00-00-00', useFixedWidthFont=True, size=(80, -1), ) hb.Add(self.impinjHostName) hb.Add(wx.StaticText(self, label=ImpinjHostNameSuffix), flag=wx.ALIGN_CENTER_VERTICAL) hb.Add(wx.StaticText(self, label=' : ' + '{}'.format(ImpinjInboundPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) iRow += 1 self.useStaticAddress = wx.RadioButton(self, label='IP:') gbs.Add(self.useStaticAddress, pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.impinjHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.impinjHost) hb.Add(wx.StaticText(self, label=' : ' + '{}'.format(ImpinjInboundPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) self.useHostName.SetValue(True) self.useStaticAddress.SetValue(False) iRow += 1 self.antennaReads = AntennaReads(self) gbs.Add(self.antennaReads, pos=(iRow, 0), span=(1, 3), flag=wx.ALIGN_LEFT | wx.EXPAND) iRow += 1 self.antennaReadCount = wx.StaticText( self, label='ANT Reads: 1:0 0% | 2:0 0% | 3:0 0% | 4:0 0% ' ) gbs.Add(self.antennaReadCount, pos=(iRow, 0), span=(1, 3), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, label='Backup File:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.backupFile = wx.StaticText( self, label=' ') gbs.Add(self.backupFile, pos=(iRow, 1), span=(1, 2), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # CrossMgr configuration. # cmcs = wx.BoxSizer(wx.VERTICAL) fgs.Add(cmcs, flag=wx.EXPAND | wx.ALL, border=4) cmcs.Add( setFont(bigFont, wx.StaticText(self, label='CrossMgr Configuration:'))) hh = wx.BoxSizer(wx.HORIZONTAL) hh.Add(wx.StaticText(self, label='CrossMgr Address:'), flag=wx.ALIGN_CENTER_VERTICAL) self.crossMgrHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hh.Add(self.crossMgrHost, flag=wx.ALIGN_LEFT) hh.Add(wx.StaticText(self, label=' : 53135'), flag=wx.ALIGN_CENTER_VERTICAL) cmcs.Add(hh, flag=wx.ALL, border=4) #------------------------------------------------------------------------------------------------ # Add strays # cmcs.Add(wx.StaticLine(self, style=wx.LI_HORIZONTAL), flag=wx.EXPAND | wx.TOP | wx.BOTTOM, border=2) self.strayTagsLabel = wx.StaticText(self, label='Stray Tags: ') cmcs.Add(self.strayTagsLabel, flag=wx.LEFT | wx.RIGHT, border=4) self.strays = wx.ListCtrl(self, style=wx.LC_REPORT | wx.BORDER_SUNKEN, size=(-1, 50)) self.strays.InsertColumn(0, 'Tag', wx.LIST_AUTOSIZE_USEHEADER) self.strays.InsertColumn(1, 'Time', wx.LIST_AUTOSIZE_USEHEADER) cmcs.Add(self.strays, 1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border=4) #------------------------------------------------------------------------------------------------ # Add messages # self.impinjMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.impinjMessagesText, flag=wx.EXPAND, proportion=2) self.impinjMessages = MessageManager(self.impinjMessagesText) self.crossMgrMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.crossMgrMessagesText, flag=wx.EXPAND, proportion=2) self.crossMgrMessages = MessageManager(self.crossMgrMessagesText) self.fgs = fgs #------------------------------------------------------------------------------------------------ # Create a timer to update the messages. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.updateMessages) self.timer.Start(1000, False) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizer(self.vbs) self.start() def OnAboutBox(self, e): description = """CrossMgrImpinj is an Impinj interface to CrossMgr """ licence = """CrossMgrImpinjis free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. CrossMgrImpinj is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with File Hunter; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA""" info = wx.adv.AboutDialogInfo() crossMgrPng = Utils.getImageFolder() + '/CrossMgrImpinj.png' info.SetIcon(wx.Icon(crossMgrPng, wx.BITMAP_TYPE_PNG)) info.SetName('CrossMgrImpinj') info.SetVersion(AppVerName.split(' ')[1]) info.SetDescription(description) info.SetCopyright('(C) 2020 Edward Sitarski') info.SetWebSite('http://www.sites.google.com/site/crossmgrsoftware/') info.SetLicence(licence) wx.adv.AboutBox(info, self) def readerStatusCB(self, **kwargs): # As this is called from another thread, make sure all UI updates are done from CallAfter. connectedAntennas = set(kwargs.get('connectedAntennas', [])) for i in range(4): wx.CallAfter( self.antennaLabels[i].SetBackgroundColour, self.LightGreen if (i + 1) in connectedAntennas else wx.NullColour) def refreshMethodName(self): if Impinj.ProcessingMethod == 0 and QuadReg.samplesTotal: s = u'{}: Inliers: {:.1%}'.format( MethodNames[Impinj.ProcessingMethod], float(QuadReg.inliersTotal) / float(QuadReg.samplesTotal)) else: s = MethodNames[Impinj.ProcessingMethod] self.methodName.SetLabel(s) def refreshStrays(self, strays): if self.strays.GetItemCount() != len(strays): self.strayTagsLabel.SetLabel('Stray Tags: {}'.format(len(strays))) if not strays: if self.strays.GetItemCount(): self.strays.DeleteAllItems() return if (self.strays.GetItemCount() == len(strays) and strays[0][0] == self.strays.GetItemText(0) and strays[-1][0] == self.strays.GetItemText(self.strays.GetItemCount() - 1)): return self.strays.DeleteAllItems() for tag, discovered in strays: i = self.strays.InsertItem(1000000, tag) self.strays.SetItem(i, 1, discovered.strftime('%H:%M:%S')) for c in range(self.strays.GetColumnCount()): self.strays.SetColumnWidth(c, wx.LIST_AUTOSIZE_USEHEADER) def strayHandler(self, strayQ): while 1: msg = strayQ.get() if msg[0] == 'strays': wx.CallAfter(self.refreshStrays, msg[1]) elif msg[0] == 'shutdown': break def start(self): self.dataQ = Queue() self.strayQ = Queue() self.messageQ = Queue() self.shutdownQ = Queue( ) # Queue to tell the Impinj monitor to shut down. self.readerStatusCB() if self.useHostName.GetValue(): self.impinjProcess = Process( name='ImpinjProcess', target=ImpinjServer, args=( self.dataQ, self.strayQ, self.messageQ, self.shutdownQ, ImpinjHostNamePrefix + self.impinjHostName.GetValue() + ImpinjHostNameSuffix, ImpinjInboundPort, self.getAntennaStr(), self.readerStatusCB, )) else: self.impinjProcess = Process(name='ImpinjProcess', target=ImpinjServer, args=( self.dataQ, self.strayQ, self.messageQ, self.shutdownQ, self.impinjHost.GetAddress(), ImpinjInboundPort, self.getAntennaStr(), self.readerStatusCB, )) self.impinjProcess.daemon = True self.strayProcess = Process(name='StrayProcess', target=self.strayHandler, args=(self.strayQ, )) self.strayProcess.daemon = True self.crossMgrProcess = Process(name='CrossMgrProcess', target=CrossMgrServer, args=(self.dataQ, self.messageQ, self.shutdownQ, self.getCrossMgrHost(), CrossMgrPort)) self.crossMgrProcess.daemon = True self.impinjProcess.start() self.strayProcess.start() self.crossMgrProcess.start() def shutdown(self): self.impinjProcess = None self.crossMgrProcess = None self.strayProcess = None self.messageQ = None self.dataQ = None self.strayQ = None self.shutdownQ = None def doReset(self, event, confirm=True): if confirm: dlg = wx.MessageDialog(self, 'Reset CrossMgrImpinj Adapter?', 'Confirm Reset', wx.OK | wx.CANCEL | wx.ICON_WARNING) ret = dlg.ShowModal() dlg.Destroy() if ret != wx.ID_OK: return self.reset.Enable( False) # Prevent multiple clicks while shutting down. self.writeOptions() self.refreshMethodName() self.gracefulShutdown() self.impinjMessages.clear() self.crossMgrMessages.clear() self.shutdown() self.reset.Enable(True) QuadReg.ResetStats() wx.CallAfter(self.start) def doAutoDetect(self, event): wx.BeginBusyCursor() self.gracefulShutdown() self.shutdown() impinjHost, crossMgrHost = AutoDetect( ImpinjInboundPort)[0], '127.0.0.1' wx.EndBusyCursor() if impinjHost and crossMgrHost: self.useStaticAddress.SetValue(True) self.useHostName.SetValue(False) self.impinjHost.SetValue(impinjHost) self.crossMgrHost.SetValue(crossMgrHost) else: dlg = wx.MessageDialog( self, 'Auto Detect Failed.\nCheck that reader has power and is connected to the router.', 'Auto Detect Failed', wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() self.doReset(event, False) def doAdvanced(self, event): dlg = AdvancedSetup(self) if dlg.ShowModal() == wx.ID_OK: if Utils.MessageOKCancel(self, 'Reset Reader Now?', 'Reset Reader'): self.doReset(event, confirm=False) dlg.Destroy() def gracefulShutdown(self): # Shutdown the CrossMgr process by sending it a shutdown command. if self.shutdownQ: self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') if self.dataQ: self.dataQ.put('shutdown') self.dataQ.put('shutdown') if self.strayQ: self.strayQ.put(('shutdown', )) if self.crossMgrProcess: self.crossMgrProcess.join() if self.impinjProcess: self.impinjProcess.join() if self.strayProcess: self.strayProcess.join() self.crossMgrProcess = None self.impinjProcess = None self.strayProcess = None def onCloseWindow(self, event): self.gracefulShutdown() wx.Exit() def doCopyToClipboard(self, event): cc = [ AppVerName, 'Configuration: CrossMgrImpinj', ' RunningTime: {}'.format(self.runningTime.GetLabel()), ' Time: {}'.format(self.time.GetLabel()), ' BackupFile: {}'.format(self.backupFile.GetLabel()), '', 'Configuration: Impinj:', ' Use Host Name: {}'.format( 'True' if self.useHostName.GetValue() else 'False'), ' HostName: {}'.format((ImpinjHostNamePrefix + self.impinjHostName.GetValue()) + ImpinjHostNameSuffix), ' ImpinjHost: {}'.format(self.impinjHost.GetAddress()), ' ImpinjPort: {}'.format(ImpinjInboundPort), ' ReportMethod: {}'.format( MethodNames[Impinj.ProcessingMethod]), ' AntennaChoice: {}'.format( AntennaChoiceNames[Impinj.AntennaChoice]), ' RemoveOutliers:{}'.format(Impinj.RepeatSeconds), '', ' ConnectionTimeoutSeconds: {}'.format( Impinj.ConnectionTimeoutSeconds), ' KeepaliveSeconds: {}'.format(Impinj.KeepaliveSeconds), ' RepeatSeconds: {}'.format(Impinj.RepeatSeconds), '', 'Configuration: CrossMgr', ' CrossMgrHost: {}'.format(self.getCrossMgrHost()), ' CrossMgrPort: {}'.format(CrossMgrPort), ] cc.append('\nLog: Impinj') log = self.impinjMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: CrossMgr') log = self.crossMgrMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: Application\n') try: with open(redirectFileName, 'r') as fp: for line in fp: cc.append(line) except: pass if wx.TheClipboard.Open(): do = wx.TextDataObject() do.SetText('\n'.join(cc)) wx.TheClipboard.SetData(do) wx.TheClipboard.Close() dlg = wx.MessageDialog( self, 'Configuration and Logs copied to the Clipboard.', 'Copy to Clipboard Succeeded', wx.OK | wx.ICON_INFORMATION) ret = dlg.ShowModal() dlg.Destroy() else: # oops... something went wrong! wx.MessageBox("Unable to open the clipboard", "Error") def getCrossMgrHost(self): return self.crossMgrHost.GetAddress() def getAntennaStr(self): selectedAntennas = [i for i in range(4) if self.antennas[i].GetValue()] # Ensure at least one antenna is selected. if not selectedAntennas: self.antennas[0].SetValue(True) selectedAntennas = [0] return ' '.join('{}'.format(a + 1) for a in selectedAntennas) def setAntennaStr(self, s): antennas = set(int(a) for a in s.split()) for i in range(4): self.antennas[i].SetValue((i + 1) in antennas) def writeOptions(self): self.config.Write('CrossMgrHost', self.getCrossMgrHost()) self.config.Write('UseHostName', 'True' if self.useHostName.GetValue() else 'False') self.config.Write( 'ImpinjHostName', ImpinjHostNamePrefix + self.impinjHostName.GetValue() + ImpinjHostNameSuffix) self.config.Write('ImpinjAddr', self.impinjHost.GetAddress()) self.config.Write('ImpinjPort', '{}'.format(ImpinjInboundPort)) self.config.Write('Antennas', self.getAntennaStr()) self.config.Write('ConnectionTimeoutSeconds', '{}'.format(Impinj.ConnectionTimeoutSeconds)) self.config.Write('KeepaliveSeconds', '{}'.format(Impinj.KeepaliveSeconds)) self.config.Write('RepeatSeconds', '{}'.format(Impinj.RepeatSeconds)) self.config.Write('PlaySounds', '{}'.format(Utils.playBell)) self.config.Write('ReceiverSensitivity', '{}'.format(Impinj.ReceiverSensitivity or 0)) self.config.Write('TransmitPower', '{}'.format(Impinj.TransmitPower or 0)) self.config.Write('TagPopulation', '{}'.format(Impinj.TagPopulation or 0)) self.config.Write('TagTransitTime', '{}'.format(Impinj.TagTransitTime or 0)) self.config.Write('ProcessingMethod', '{}'.format(Impinj.ProcessingMethod)) self.config.Write('AntennaChoice', '{}'.format(Impinj.AntennaChoice)) self.config.Write( 'RemoveOutliers', '{}'.format('True' if Impinj.RemoveOutliers else 'False')) self.config.Flush() def readOptions(self): self.crossMgrHost.SetValue( self.config.Read('CrossMgrHost', Utils.DEFAULT_HOST)) useHostName = (self.config.Read('UseHostName', 'True').upper()[:1] == 'T') self.useHostName.SetValue(useHostName) self.useStaticAddress.SetValue(not useHostName) self.impinjHostName.SetValue( self.config.Read( 'ImpinjHostName', ImpinjHostNamePrefix + '00-00-00' + ImpinjHostNameSuffix) [len(ImpinjHostNamePrefix):-len(ImpinjHostNameSuffix)]) self.impinjHost.SetValue(self.config.Read('ImpinjAddr', '0.0.0.0')) self.setAntennaStr(self.config.Read('Antennas', '1 2 3 4')) Utils.playBell = (self.config.Read('PlaySounds', 'True').upper()[:1] == 'T') Impinj.ConnectionTimeoutSeconds = int( self.config.Read('ConnectionTimeoutSeconds', '{}'.format(Impinj.ConnectionTimeoutSeconds))) Impinj.KeepaliveSeconds = int( self.config.Read('KeepaliveSeconds', '{}'.format(Impinj.KeepaliveSeconds))) Impinj.RepeatSeconds = int( self.config.Read('RepeatSeconds', '{}'.format(Impinj.RepeatSeconds))) Impinj.ReceiverSensitivity = int( self.config.Read('ReceiverSensitivity', '0')) or None Impinj.TransmitPower = int(self.config.Read('TransmitPower', '0')) or None Impinj.TagPopulation = int(self.config.Read('TagPopulation', '0')) or None Impinj.TagTransitTime = int(self.config.Read('TagTransitTime', '0')) or None Impinj.ProcessingMethod = int(self.config.Read( 'ProcessingMethod', '0')) # Default to QuadraticRegression. Impinj.AntennaChoice = int(self.config.Read( 'AntennaChoice', '1')) # Default to MaxDB antenna. Impinj.RemoveOutliers = (self.config.Read('RemoveOutliers', 'True').upper()[:1] == 'T') def updateMessages(self, event): tNow = datetime.datetime.now() running = int((tNow - self.tStart).total_seconds()) self.runningTime.SetLabel('{:02d}:{:02d}:{:02d}'.format( running // (60 * 60), (running // 60) % 60, running % 60)) self.time.SetLabel(tNow.strftime('%H:%M:%S')) def formatAntennaReadCount(arc): if arc < 1000: return arc if arc < 1000000: return '{:.1f}k'.format(arc / 1000.0) return '{:.1f}m'.format(arc / 1000000.0) if not self.messageQ: return while 1: try: d = self.messageQ.get(False) except Empty: break if isinstance(d[-1], dict): antennaReadCount = d[-1] d = d[:-1] else: antennaReadCount = None message = ' '.join(six.text_type(x) for x in d[1:]) if d[0] == 'Impinj': if 'state' in d: self.impinjMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.impinjMessages.write(message) if antennaReadCount is not None: total = max( 1, sum(antennaReadCount[i] for i in range(1, 4 + 1))) label = '{}: {} ({})'.format( 'ANT Used' if Impinj.ProcessingMethod != FirstReadMethod else 'ANT Reads', ' | '.join('{}:{} {:.1f}%'.format( i, formatAntennaReadCount(antennaReadCount[i]), antennaReadCount[i] * 100.0 / total) for i in range(1, 4 + 1)), 'Peak RSSI' if Impinj.ProcessingMethod != FirstReadMethod else 'First Read', ) self.antennaReadCount.SetLabel(label) self.antennaReads.Set( [antennaReadCount[i] for i in range(1, 4 + 1)]) self.refreshMethodName() elif d[0] == 'Impinj2JChip': if 'state' in d: self.crossMgrMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.crossMgrMessages.write(message) elif d[0] == 'BackupFile': self.backupFile.SetLabel(d[1])
class MainWin(wx.Frame): def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) self.config = wx.Config( appName="CrossMgrImpinj", vendorName="SmartCyclingSolutions", #style=wx.Config.CONFIG_USE_LOCAL_FILE ) self.SetBackgroundColour(wx.Colour(232, 232, 232)) self.LightGreen = wx.Colour(153, 255, 153) self.LightRed = wx.Colour(255, 153, 153) font = self.GetFont() bigFont = wx.Font(int(font.GetPointSize() * 1.5), font.GetFamily(), font.GetStyle(), wx.FONTWEIGHT_BOLD) titleFont = wx.Font(int(bigFont.GetPointSize() * 2.2), bigFont.GetFamily(), bigFont.GetStyle(), bigFont.GetWeight()) self.vbs = wx.BoxSizer(wx.VERTICAL) bs = wx.BoxSizer(wx.HORIZONTAL) self.reset = RoundButton(self, label='Reset', size=(80, 80)) self.reset.SetBackgroundColour(wx.WHITE) self.reset.SetForegroundColour(wx.Colour(0, 128, 128)) self.reset.SetFontToFitLabel( ) # Use the button's default font, but change the font size to fit the label. self.reset.Bind(wx.EVT_BUTTON, self.doReset) self.reset.Refresh() bs.Add(self.reset, border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(titleFont, wx.StaticText(self, label='CrossMgrImpinj')), border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.AddStretchSpacer() bitmap = wx.Bitmap(clipboard_xpm) self.copyToClipboard = wx.BitmapButton(self, bitmap=bitmap) self.copyToClipboard.SetToolTip( wx.ToolTip('Copy Configuration and Logs to Clipboard...')) self.copyToClipboard.Bind(wx.EVT_BUTTON, self.doCopyToClipboard) bs.Add(self.copyToClipboard, border=32, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.tStart = datetime.datetime.now() bs.Add(setFont( bigFont, wx.StaticText(self, label='Last Reset: %s' % self.tStart.strftime('%H:%M:%S'))), border=10, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.runningTime = setFont(bigFont, wx.StaticText(self, label='00:00:00')) bs.Add(self.runningTime, border=20, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(bigFont, wx.StaticText(self, label=' / ')), flag=wx.ALIGN_CENTER_VERTICAL) self.time = setFont(bigFont, wx.StaticText(self, label='00:00:00')) bs.Add(self.time, flag=wx.ALIGN_CENTER_VERTICAL) self.vbs.Add(bs, flag=wx.ALL | wx.EXPAND, border=4) fgs = wx.FlexGridSizer(rows=2, cols=2, vgap=4, hgap=4) fgs.AddGrowableRow(1) fgs.AddGrowableCol(0) fgs.AddGrowableCol(1) fgs.SetFlexibleDirection(wx.BOTH) self.vbs.Add(fgs, flag=wx.EXPAND, proportion=5) #------------------------------------------------------------------------------------------------ # Impinj configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) iRow = 0 hb = wx.BoxSizer(wx.HORIZONTAL) hb.Add(setFont(bigFont, wx.StaticText(self, label='Impinj Configuration:')), flag=wx.ALIGN_CENTER_VERTICAL) self.autoDetectButton = wx.Button(self, label='Auto Detect') self.autoDetectButton.Bind(wx.EVT_BUTTON, self.doAutoDetect) hb.Add(self.autoDetectButton, flag=wx.LEFT, border=6) self.advancedButton = wx.Button(self, label='Advanced...') self.advancedButton.Bind(wx.EVT_BUTTON, self.doAdvanced) hb.Add(self.advancedButton, flag=wx.LEFT, border=6) gbs.Add(hb, pos=(iRow, 0), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gs = wx.GridSizer(rows=0, cols=4, vgap=0, hgap=2) self.antennaLabels = [] self.antennas = [] for i in xrange(4): self.antennaLabels.append( wx.StaticText(self, label='{}'.format(i + 1), style=wx.ALIGN_CENTER)) gs.Add(self.antennaLabels[-1], flag=wx.ALIGN_CENTER | wx.EXPAND) for i in xrange(4): cb = wx.CheckBox(self, wx.ID_ANY, '') if i < 2: cb.SetValue(True) cb.Bind(wx.EVT_CHECKBOX, lambda x: self.getAntennaStr()) gs.Add(cb, flag=wx.ALIGN_CENTER) self.antennas.append(cb) gbs.Add(wx.StaticText(self, label='ANT Ports:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) gbs.Add(gs, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) iRow += 1 self.useHostName = wx.RadioButton(self, label='Host Name:', style=wx.RB_GROUP) gbs.Add(self.useHostName, pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) hb.Add(wx.StaticText(self, label=ImpinjHostNamePrefix), flag=wx.ALIGN_CENTER_VERTICAL) self.impinjHostName = masked.TextCtrl( self, mask='NN-NN-NN', defaultValue='00-00-00', useFixedWidthFont=True, size=(80, -1), ) hb.Add(self.impinjHostName) hb.Add(wx.StaticText(self, label=ImpinjHostNameSuffix), flag=wx.ALIGN_CENTER_VERTICAL) hb.Add(wx.StaticText(self, label=' : ' + '{}'.format(ImpinjInboundPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) iRow += 1 self.useStaticAddress = wx.RadioButton(self, label='IP:') gbs.Add(self.useStaticAddress, pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.impinjHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.impinjHost) hb.Add(wx.StaticText(self, label=' : ' + '{}'.format(ImpinjInboundPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) self.useHostName.SetValue(True) self.useStaticAddress.SetValue(False) iRow += 1 gbs.Add(wx.StaticText(self, label='ANT Reads:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.antennaReadCount = wx.StaticText( self, label='1:0 0% | 2:0 0% | 3:0 0% | 4:0 0% ') gbs.Add(self.antennaReadCount, pos=(iRow, 1), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, label='Backup File:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.backupFile = wx.StaticText( self, label=' ') gbs.Add(self.backupFile, pos=(iRow, 1), span=(1, 2), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # CrossMgr configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) gbs.Add(setFont(bigFont, wx.StaticText(self, label='CrossMgr Configuration:')), pos=(0, 0), span=(1, 2), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL) gbs.Add(wx.StaticText(self, label='CrossMgr Address:'), pos=(1, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.crossMgrHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.crossMgrHost, flag=wx.ALIGN_LEFT) hb.Add(wx.StaticText(self, label=' : 53135'), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(1, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # Add messages # self.impinjMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.impinjMessagesText, flag=wx.EXPAND, proportion=2) self.impinjMessages = MessageManager(self.impinjMessagesText) self.crossMgrMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.crossMgrMessagesText, flag=wx.EXPAND, proportion=2) self.crossMgrMessages = MessageManager(self.crossMgrMessagesText) self.fgs = fgs #------------------------------------------------------------------------------------------------ # Create a timer to update the messages. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.updateMessages) self.timer.Start(1000, False) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizer(self.vbs) self.start() def readerStatusCB(self, **kwargs): # As this is called from another thread, make sure all UI updates are done from CallAfter. connectedAntennas = set(kwargs.get('connectedAntennas', [])) for i in xrange(4): wx.CallAfter( self.antennaLabels[i].SetBackgroundColour, self.LightGreen if (i + 1) in connectedAntennas else wx.NullColour) def start(self): self.dataQ = Queue() self.messageQ = Queue() self.shutdownQ = Queue( ) # Queue to tell the Impinj monitor to shut down. self.readerStatusCB() if self.useHostName.GetValue(): self.impinjProcess = Process( name='ImpinjProcess', target=ImpinjServer, args=( self.dataQ, self.messageQ, self.shutdownQ, ImpinjHostNamePrefix + self.impinjHostName.GetValue() + ImpinjHostNameSuffix, ImpinjInboundPort, self.getAntennaStr(), self.readerStatusCB, )) else: self.impinjProcess = Process(name='ImpinjProcess', target=ImpinjServer, args=( self.dataQ, self.messageQ, self.shutdownQ, self.impinjHost.GetAddress(), ImpinjInboundPort, self.getAntennaStr(), self.readerStatusCB, )) self.impinjProcess.daemon = True self.crossMgrProcess = Process(name='CrossMgrProcess', target=CrossMgrServer, args=(self.dataQ, self.messageQ, self.shutdownQ, self.getCrossMgrHost(), CrossMgrPort)) self.crossMgrProcess.daemon = True self.impinjProcess.start() self.crossMgrProcess.start() def shutdown(self): self.impinjProcess = None self.crossMgrProcess = None self.messageQ = None self.dataQ = None self.shutdownQ = None def doReset(self, event, confirm=True): if confirm: dlg = wx.MessageDialog(self, 'Reset CrossMgrImpinj Adapter?', 'Confirm Reset', wx.OK | wx.CANCEL | wx.ICON_WARNING) ret = dlg.ShowModal() dlg.Destroy() if ret != wx.ID_OK: return self.reset.Enable( False) # Prevent multiple clicks while shutting down. self.writeOptions() self.gracefulShutdown() self.impinjMessages.clear() self.crossMgrMessages.clear() self.shutdown() self.reset.Enable(True) wx.CallAfter(self.start) def doAutoDetect(self, event): wx.BeginBusyCursor() self.gracefulShutdown() self.shutdown() impinjHost, crossMgrHost = AutoDetect( ImpinjInboundPort)[0], '127.0.0.1' wx.EndBusyCursor() if impinjHost and crossMgrHost: self.useStaticAddress.SetValue(True) self.useHostName.SetValue(False) self.impinjHost.SetValue(impinjHost) self.crossMgrHost.SetValue(crossMgrHost) else: dlg = wx.MessageDialog( self, 'Auto Detect Failed.\nCheck that reader has power and is connected to the router.', 'Auto Detect Failed', wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() self.doReset(event, False) def doAdvanced(self, event): dlg = AdvancedSetup(self) dlg.ShowModal() dlg.Destroy() def gracefulShutdown(self): # Shutdown the CrossMgr process by sending it a shutdown command. if self.shutdownQ: self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') if self.dataQ: self.dataQ.put('shutdown') self.dataQ.put('shutdown') if self.crossMgrProcess: self.crossMgrProcess.join() if self.impinjProcess: self.impinjProcess.join() self.crossMgrProcess = None self.impinjProcess = None def onCloseWindow(self, event): self.gracefulShutdown() wx.Exit() def doCopyToClipboard(self, event): cc = [ 'Configuration: CrossMgrImpinj', ' RunningTime: {}'.format(self.runningTime.GetLabel()), ' Time: {}'.format(self.time.GetLabel()), ' BackupFile: {}'.format(self.backupFile.GetLabel()), '', 'Configuration: Impinj:', ' Use Host Name: {}'.format( 'True' if self.useHostName.GetValue() else 'False'), ' HostName: {}'.format((ImpinjHostNamePrefix + self.impinjHostName.GetValue()) + ImpinjHostNameSuffix), ' ImpinjHost: {}'.format(self.impinjHost.GetAddress()), ' ImpinjPort: {}'.format(ImpinjInboundPort), '' ' ConnectionTimeoutSeconds: {}'.format( Impinj.ConnectionTimeoutSeconds), ' KeepaliveSeconds: {}'.format(Impinj.KeepaliveSeconds), ' RepeatSeconds: {}'.format(Impinj.RepeatSeconds), '', 'Configuration: CrossMgr', ' CrossMgrHost: {}'.format(self.getCrossMgrHost()), ' CrossMgrPort: {}'.format(CrossMgrPort), ] cc.append('\nLog: Impinj') log = self.impinjMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: CrossMgr') log = self.crossMgrMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: Application\n') try: with open(redirectFileName, 'r') as fp: for line in fp: cc.append(line) except: pass if wx.TheClipboard.Open(): do = wx.TextDataObject() do.SetText('\n'.join(cc)) wx.TheClipboard.SetData(do) wx.TheClipboard.Close() dlg = wx.MessageDialog( self, 'Configuration and Logs copied to the Clipboard.', 'Copy to Clipboard Succeeded', wx.OK | wx.ICON_INFORMATION) ret = dlg.ShowModal() dlg.Destroy() else: # oops... something went wrong! wx.MessageBox("Unable to open the clipboard", "Error") def getCrossMgrHost(self): return self.crossMgrHost.GetAddress() def getAntennaStr(self): selectedAntennas = [ i for i in xrange(4) if self.antennas[i].GetValue() ] # Ensure at least one antenna is selected. if not selectedAntennas: self.antennas[0].SetValue(True) selectedAntennas = [0] return ' '.join('{}'.format(a + 1) for a in selectedAntennas) def setAntennaStr(self, s): antennas = set(int(a) for a in s.split()) for i in xrange(4): self.antennas[i].SetValue((i + 1) in antennas) def writeOptions(self): self.config.Write('CrossMgrHost', self.getCrossMgrHost()) self.config.Write('UseHostName', 'True' if self.useHostName.GetValue() else 'False') self.config.Write( 'ImpinjHostName', ImpinjHostNamePrefix + self.impinjHostName.GetValue() + ImpinjHostNameSuffix) self.config.Write('ImpinjAddr', self.impinjHost.GetAddress()) self.config.Write('ImpinjPort', '{}'.format(ImpinjInboundPort)) self.config.Write('Antennas', self.getAntennaStr()) self.config.Write('ConnectionTimeoutSeconds', '{}'.format(Impinj.ConnectionTimeoutSeconds)) self.config.Write('KeepaliveSeconds', '{}'.format(Impinj.KeepaliveSeconds)) self.config.Write('RepeatSeconds', '{}'.format(Impinj.RepeatSeconds)) self.config.Write('PlaySounds', '{}'.format(Utils.playBell)) self.config.Write('ReceiverSensitivity', '{}'.format(Impinj.ReceiverSensitivity or 0)) self.config.Write('TransmitPower', '{}'.format(Impinj.TransmitPower or 0)) self.config.Write('TagPopulation', '{}'.format(Impinj.TagPopulation or 0)) self.config.Write('TagTransitTime', '{}'.format(Impinj.TagTransitTime or 0)) self.config.Flush() def readOptions(self): self.crossMgrHost.SetValue( self.config.Read('CrossMgrHost', Utils.DEFAULT_HOST)) useHostName = (self.config.Read('UseHostName', 'True').upper()[:1] == 'T') self.useHostName.SetValue(useHostName) self.useStaticAddress.SetValue(not useHostName) self.impinjHostName.SetValue( self.config.Read( 'ImpinjHostName', ImpinjHostNamePrefix + '00-00-00' + ImpinjHostNameSuffix) [len(ImpinjHostNamePrefix):-len(ImpinjHostNameSuffix)]) self.impinjHost.SetValue(self.config.Read('ImpinjAddr', '0.0.0.0')) self.setAntennaStr(self.config.Read('Antennas', '1 2 3 4')) Utils.playBell = (self.config.Read('PlaySounds', 'True').upper()[:1] == 'T') Impinj.ConnectionTimeoutSeconds = int( self.config.Read('ConnectionTimeoutSeconds', '{}'.format(Impinj.ConnectionTimeoutSeconds))) Impinj.KeepaliveSeconds = int( self.config.Read('KeepaliveSeconds', '{}'.format(Impinj.KeepaliveSeconds))) Impinj.RepeatSeconds = int( self.config.Read('RepeatSeconds', '{}'.format(Impinj.RepeatSeconds))) Impinj.ReceiverSensitivity = int( self.config.Read('ReceiverSensitivity', '0')) or None Impinj.TransmitPower = int(self.config.Read('TransmitPower', '0')) or None Impinj.TagPopulation = int(self.config.Read('TagPopulation', '0')) or None Impinj.TagTransitTime = int(self.config.Read('TagTransitTime', '0')) or None def updateMessages(self, event): tNow = datetime.datetime.now() running = int((tNow - self.tStart).total_seconds()) self.runningTime.SetLabel('{:02d}:{:02d}:{:02d}'.format( running // (60 * 60), (running // 60) % 60, running % 60)) self.time.SetLabel(tNow.strftime('%H:%M:%S')) if not self.messageQ: return while 1: try: d = self.messageQ.get(False) except Empty: break if isinstance(d[-1], dict): antennaReadCount = d[-1] d = d[:-1] else: antennaReadCount = None message = ' '.join(unicode(x) for x in d[1:]) if d[0] == 'Impinj': if 'state' in d: self.impinjMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.impinjMessages.write(message) if antennaReadCount is not None: total = max( 1, sum(antennaReadCount[i] for i in xrange(1, 4 + 1))) self.antennaReadCount.SetLabel(' | '.join( '{}:{} {:.1f}%'.format( i, antennaReadCount[i], antennaReadCount[i] * 100.0 / total) for i in xrange(1, 4 + 1))) elif d[0] == 'Impinj2JChip': if 'state' in d: self.crossMgrMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.crossMgrMessages.write(message) elif d[0] == 'BackupFile': self.backupFile.SetLabel(d[1])
class MainWin(wx.Frame): def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) self.config = wx.Config( appName="CrossMgrAlien", vendorName="SmartCyclingSolutions", #style=wx.CONFIG_USE_LOCAL_FILE ) self.SetBackgroundColour(wx.Colour(232, 232, 232)) self.LightGreen = wx.Colour(153, 255, 153) self.LightRed = wx.Colour(255, 153, 153) font = self.GetFont() bigFont = wx.Font(font.GetPointSize() * 1.5, font.GetFamily(), font.GetStyle(), wx.FONTWEIGHT_BOLD) titleFont = wx.Font(bigFont.GetPointSize() * 2.2, bigFont.GetFamily(), bigFont.GetStyle(), bigFont.GetWeight()) self.vbs = wx.BoxSizer(wx.VERTICAL) bs = wx.BoxSizer(wx.HORIZONTAL) self.reset = RoundButton(self, wx.ID_ANY, 'Reset', size=(80, 80)) self.reset.SetBackgroundColour(wx.WHITE) self.reset.SetForegroundColour(wx.Colour(0, 128, 128)) self.reset.SetFontToFitLabel( ) # Use the button's default font, but change the font size to fit the label. self.reset.Bind(wx.EVT_BUTTON, self.doReset) self.reset.Refresh() bs.Add(self.reset, border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(titleFont, wx.StaticText(self, wx.ID_ANY, 'CrossMgrAlien')), border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.AddStretchSpacer() bitmap = wx.Bitmap(clipboard_xpm) self.copyToClipboard = wx.BitmapButton(self, wx.ID_ANY, bitmap) self.copyToClipboard.SetToolTip( wx.ToolTip('Copy Configuration and Logs to Clipboard...')) self.copyToClipboard.Bind(wx.EVT_BUTTON, self.doCopyToClipboard) bs.Add(self.copyToClipboard, border=32, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.tStart = datetime.datetime.now() bs.Add(setFont( bigFont, wx.StaticText( self, wx.ID_ANY, 'Last Reset: {}'.format(self.tStart.strftime('%H:%M:%S')))), border=10, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.runningTime = setFont(bigFont, wx.StaticText(self, wx.ID_ANY, '00:00:00')) bs.Add(self.runningTime, border=20, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(bigFont, wx.StaticText(self, wx.ID_ANY, ' / ')), flag=wx.ALIGN_CENTER_VERTICAL) self.time = setFont(bigFont, wx.StaticText(self, wx.ID_ANY, '00:00:00')) bs.Add(self.time, flag=wx.ALIGN_CENTER_VERTICAL) self.vbs.Add(bs, flag=wx.ALL | wx.EXPAND, border=4) fgs = wx.FlexGridSizer(rows=0, cols=2, vgap=4, hgap=4) fgs.AddGrowableRow(1) fgs.AddGrowableCol(0) fgs.AddGrowableCol(1) fgs.SetFlexibleDirection(wx.BOTH) self.vbs.Add(fgs, flag=wx.EXPAND, proportion=5) #------------------------------------------------------------------------------------------------ # Alien configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) iRow = 0 hs = wx.BoxSizer(wx.HORIZONTAL) hs.Add(setFont(bigFont, wx.StaticText(self, wx.ID_ANY, 'Alien Configuration:')), flag=wx.ALIGN_CENTER_VERTICAL) self.autoDetectButton = wx.Button(self, wx.ID_ANY, 'Auto Detect') self.autoDetectButton.Bind(wx.EVT_BUTTON, self.doAutoDetect) hs.Add(self.autoDetectButton, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=6) self.advancedButton = wx.Button(self, wx.ID_ANY, 'Advanced...') self.advancedButton.Bind(wx.EVT_BUTTON, self.doAdvanced) hs.Add(self.advancedButton, flag=wx.LEFT, border=6) gbs.Add(hs, pos=(iRow, 0), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, wx.ID_ANY, 'Antennas:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) gs = wx.GridSizer(rows=0, cols=4, hgap=2, vgap=2) self.antennas = [] for i in range(4): gs.Add(wx.StaticText(self, wx.ID_ANY, '{}'.format(i)), flag=wx.ALIGN_CENTER) for i in range(4): cb = wx.CheckBox(self, wx.ID_ANY, '') if i < 2: cb.SetValue(True) cb.Bind(wx.EVT_CHECKBOX, lambda x: self.getAntennaStr()) gs.Add(cb, flag=wx.ALIGN_CENTER) self.antennas.append(cb) gbs.Add(gs, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) iRow += 1 gbs.Add(wx.StaticText(self, wx.ID_ANY, 'Notify Address:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) ips = Utils.GetAllIps() self.notifyHost = wx.Choice(self, choices=ips) hb.Add(self.notifyHost) hb.Add(wx.StaticText(self, label=' : {}'.format(NotifyPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1)) iRow += 1 self.listenForHeartbeat = wx.CheckBox( self, label='Listen for Alien Heartbeat on Port: {}'.format( HeartbeatPort), style=wx.ALIGN_LEFT) self.listenForHeartbeat.SetValue(True) gbs.Add(self.listenForHeartbeat, pos=(iRow, 0), span=(1, 2)) iRow += 1 gbs.Add(wx.StaticText(self, label='Alien Cmd Address:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.cmdHost = IpAddrCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_TAB) hb.Add(self.cmdHost) hb.Add(wx.StaticText(self, label=' : '), flag=wx.ALIGN_CENTER_VERTICAL) self.cmdPort = intctrl.IntCtrl(self, size=(50, -1), min=0, max=999999) hb.Add(self.cmdPort, flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, label='Backup File:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.backupFile = wx.StaticText(self, label='') gbs.Add(self.backupFile, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # CrossMgr configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) gbs.Add(setFont(bigFont, wx.StaticText(self, label='CrossMgr Configuration:')), pos=(0, 0), span=(1, 2), flag=wx.ALIGN_LEFT) gbs.Add(wx.StaticText(self, label='CrossMgr Address:'), pos=(1, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.crossMgrHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.crossMgrHost, flag=wx.ALIGN_LEFT) hb.Add(wx.StaticText(self, label=' : 53135'), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(1, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # Add messages # self.alienMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.alienMessagesText, flag=wx.EXPAND, proportion=2) self.alienMessages = MessageManager(self.alienMessagesText) self.crossMgrMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.crossMgrMessagesText, flag=wx.EXPAND, proportion=2) self.crossMgrMessages = MessageManager(self.crossMgrMessagesText) self.fgs = fgs #------------------------------------------------------------------------------------------------ # Create a timer to update the messages. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.updateMessages) self.timer.Start(1000, False) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizer(self.vbs) self.start() def start(self): self.dataQ = Queue() self.messageQ = Queue() self.shutdownQ = Queue( ) # Queue to tell the Alien monitor to shut down. self.alienProcess = Process( name='AlienProcess', target=AlienServer, args=(self.dataQ, self.messageQ, self.shutdownQ, self.getNotifyHost(), NotifyPort, HeartbeatPort, self.getAntennaStr(), self.listenForHeartbeat.GetValue(), self.cmdHost.GetAddress(), self.cmdPort.GetValue())) self.alienProcess.daemon = True self.crossMgrProcess = Process(name='CrossMgrProcess', target=CrossMgrServer, args=(self.dataQ, self.messageQ, self.shutdownQ, self.getCrossMgrHost(), CrossMgrPort)) self.crossMgrProcess.daemon = True self.alienProcess.start() self.crossMgrProcess.start() def doAdvanced(self, event): dlg = AdvancedSetup(self) dlg.ShowModal() dlg.Destroy() self.writeOptions() def shutdown(self): self.alienProcess = None self.crossMgrProcess = None self.messageQ = None self.dataQ = None self.shutdownQ = None def gracefulShutdown(self): # Shutdown the CrossMgr process by sending it a shutdown command. if self.shutdownQ: self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') if self.dataQ: self.dataQ.put('shutdown') if self.crossMgrProcess: self.crossMgrProcess.join() if self.alienProcess: self.alienProcess.join() self.crossMgrProcess = None self.alienProcess = None def doReset(self, event, confirm=True): if confirm: dlg = wx.MessageDialog(self, 'Reset CrossMgrAlien Adapter?', 'Confirm Reset', wx.OK | wx.CANCEL | wx.ICON_WARNING) ret = dlg.ShowModal() dlg.Destroy() if ret != wx.ID_OK: return self.reset.Enable( False) # Prevent multiple clicks while shutting down. self.writeOptions() self.gracefulShutdown() self.alienMessages.clear() self.crossMgrMessages.clear() self.shutdown() self.reset.Enable(True) wx.CallAfter(self.start) def doAutoDetect(self, event): wx.BeginBusyCursor() self.gracefulShutdown() self.shutdown() alienHost, crossmgrHost = AutoDetect() wx.EndBusyCursor() if alienHost and crossmgrHost: self.setNotifyHost( crossmgrHost ) # Assumes CrossMgr is on the same computer as CrossMgrAlien. self.cmdHost.SetValue(alienHost) self.cmdPort.SetValue(str(DefaultAlienCmdPort)) self.crossMgrHost.SetValue(crossmgrHost) self.listenForHeartbeat.SetValue(False) else: dlg = wx.MessageDialog( self, 'Auto Detect Failed.\nCheck that reader has power and is connected to the router.', 'Auto Detect Failed', wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() self.doReset(event, False) def onCloseWindow(self, event): wx.Exit() def doCopyToClipboard(self, event): cc = [ 'Configuration: CrossMgrAlien', ' NotifyHost: {}'.format(self.getNotifyHost()), ' NotifyPort: {}'.format(NotifyPort), ' RunningTime: {}'.format(self.runningTime.GetLabel()), ' Time: {}'.format(self.time.GetLabel()), ' BackupFile: {}'.format(self.backupFile.GetLabel()), '', 'Configuration: Alien:', ' ListenForAlienHeartbeat: {}'.format( 'True' if self.listenForHeartbeat.GetValue() else 'False'), ' Antennas: {}'.format(self.getAntennaStr()), ' HeartbeatPort: {}'.format(HeartbeatPort), ' AlienCmdHost: {}'.format(self.cmdHost.GetAddress()), ' AlienCmdPort: {}'.format(self.cmdPort.GetValue()), '', ' RepeatSeconds: {}'.format(Alien.RepeatSeconds), '', 'Configuration: CrossMgr', ' CrossMgrHost: {}'.format(self.getCrossMgrHost()), ' CrossMgrPort: {}'.format(CrossMgrPort), ] cc.append('\nLog: Alien') log = self.alienMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: CrossMgr') log = self.crossMgrMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: Application\n') try: with open(redirectFileName, 'r') as fp: for line in fp: cc.append(line) except: pass if wx.TheClipboard.Open(): do = wx.TextDataObject() do.SetText('\n'.join(cc)) wx.TheClipboard.SetData(do) wx.TheClipboard.Close() dlg = wx.MessageDialog( self, 'Configuration and Logs copied to the Clipboard.', 'Copy to Clipboard Succeeded', wx.OK | wx.ICON_INFORMATION) ret = dlg.ShowModal() dlg.Destroy() else: # oops... something went wrong! wx.MessageBox("Unable to open the clipboard", "Error") def getNotifyHost(self): s = self.notifyHost.GetSelection() return self.notifyHost.GetString(s) if s != wx.NOT_FOUND else None def setNotifyHost(self, notifyHost): for i, s in enumerate(self.notifyHost.GetItems()): if s == notifyHost: self.notifyHost.SetSelection(i) return self.notifyHost.SetSelection(0) def getCrossMgrHost(self): return self.crossMgrHost.GetAddress() def getAntennaStr(self): s = [] for i in range(4): if self.antennas[i].GetValue(): s.append('%d' % i) if not s: # Ensure that at least one antenna is selected. self.antennas[0].SetValue(True) s.append('0') return ' '.join(s) def setAntennaStr(self, s): antennas = set(int(a) for a in s.split()) for i in range(4): self.antennas[i].SetValue(i in antennas) def writeOptions(self): self.config.Write('CrossMgrHost', self.getCrossMgrHost()) self.config.Write( 'ListenForAlienHeartbeat', 'True' if self.listenForHeartbeat.GetValue() else 'False') self.config.Write('AlienCmdAddr', self.cmdHost.GetAddress()) self.config.Write('AlienCmdPort', str(self.cmdPort.GetValue())) self.config.Write('Antennas', self.getAntennaStr()) self.config.Write('RepeatSeconds', '{}'.format(Alien.RepeatSeconds)) self.config.Write('PlaySounds', '{}'.format(Utils.playBell)) s = self.notifyHost.GetSelection() if s != wx.NOT_FOUND: self.config.Write('NotifyHost', self.notifyHost.GetString(s)) self.config.Flush() def readOptions(self): self.crossMgrHost.SetValue( self.config.Read('CrossMgrHost', Utils.DEFAULT_HOST)) self.listenForHeartbeat.SetValue( self.config.Read('ListenForAlienHeartbeat', 'True').upper()[:1] == 'T') Utils.playBell = (self.config.Read('PlaySounds', 'True').upper()[:1] == 'T') self.cmdHost.SetValue(self.config.Read('AlienCmdAddr', '0.0.0.0')) self.cmdPort.SetValue(int(self.config.Read('AlienCmdPort', '0'))) self.setAntennaStr(self.config.Read('Antennas', '0 1')) Alien.RepeatSeconds = int( self.config.Read('RepeatSeconds', '{}'.format(Alien.RepeatSeconds))) notifyHost = self.config.Read('NotifyHost', Utils.DEFAULT_HOST) self.setNotifyHost(notifyHost) def updateMessages(self, event): tNow = datetime.datetime.now() running = int((tNow - self.tStart).total_seconds()) self.runningTime.SetLabel('{:02d}:{:02d}:{:02d}'.format( running // (60 * 60), (running // 60) % 60, running % 60)) self.time.SetLabel(tNow.strftime('%H:%M:%S')) if not self.messageQ: return while 1: try: d = self.messageQ.get(False) except Empty: break message = ' '.join(str(x) for x in d[1:]) if d[0] == 'Alien': if 'state' in d: self.alienMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.alienMessages.write(message) elif d[0] == 'Alien2JChip': if 'state' in d: self.crossMgrMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.crossMgrMessages.write(message) elif d[0] == 'CmdHost': cmdHost, cmdPort = d[1].split(':') self.cmdHost.SetValue(cmdHost) self.cmdPort.SetValue(int(cmdPort)) elif d[0] == 'BackupFile': self.backupFile.SetLabel(d[1])
class Actions(wx.Panel): iResetStartClockOnFirstTag = 1 iSkipFirstTagRead = 2 def __init__(self, parent, id=wx.ID_ANY): wx.Panel.__init__(self, parent, id) self.SetBackgroundColour(wx.Colour(255, 255, 255)) ps = wx.BoxSizer(wx.VERTICAL) self.splitter = wx.SplitterWindow(self, wx.VERTICAL) ps.Add(self.splitter, 1, flag=wx.EXPAND) self.SetSizer(ps) #--------------------------------------------------------------------------------------------- self.leftPanel = wx.Panel(self.splitter) bs = wx.BoxSizer(wx.VERTICAL) self.leftPanel.SetSizer(bs) self.leftPanel.SetBackgroundColour(wx.Colour(255, 255, 255)) self.leftPanel.Bind(wx.EVT_SIZE, self.setWrappedRaceInfo) buttonSize = 220 self.button = RoundButton(self.leftPanel, size=(buttonSize, buttonSize)) self.button.SetLabel(FinishText) self.button.SetFontToFitLabel() self.button.SetForegroundColour(wx.Colour(128, 128, 128)) self.Bind(wx.EVT_BUTTON, self.onPress, self.button) self.clock = Clock(self, size=(190, 190), checkFunc=self.updateClock) self.clock.SetBackgroundColour(wx.WHITE) self.raceIntro = wx.StaticText(self.leftPanel, label=u'') self.raceIntro.SetFont(wx.Font(20, wx.DEFAULT, wx.NORMAL, wx.NORMAL)) self.chipTimingOptions = wx.RadioBox( self.leftPanel, label=_("Chip Timing Options"), majorDimension=1, choices=Properties.RfidProperties.choices, style=wx.RA_SPECIFY_COLS) self.Bind(wx.EVT_RADIOBOX, self.onChipTimingOptions, self.chipTimingOptions) self.settingsButton = wx.BitmapButton( self.leftPanel, bitmap=Utils.GetPngBitmap('settings-icon.png')) self.settingsButton.SetToolTip(wx.ToolTip(_('Properties Shortcut'))) self.settingsButton.Bind(wx.EVT_BUTTON, self.onShowProperties) self.startRaceTimeCheckBox = wx.CheckBox( self.leftPanel, label=_('Start Race Automatically at Future Time')) hsSettings = wx.BoxSizer(wx.HORIZONTAL) hsSettings.Add(self.settingsButton, flag=wx.ALIGN_CENTER_VERTICAL) hsSettings.Add(self.startRaceTimeCheckBox, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border=12) border = 8 hs = wx.BoxSizer(wx.HORIZONTAL) hs.Add(self.button, border=border, flag=wx.LEFT | wx.TOP) hs.Add(self.raceIntro, 1, border=border, flag=wx.LEFT | wx.TOP | wx.RIGHT | wx.EXPAND) bs.Add(hs, border=border, flag=wx.ALL) hsClock = wx.BoxSizer(wx.HORIZONTAL) hsClock.AddSpacer(26) hsClock.Add(self.clock) hsClock.Add(hsSettings, border=4, flag=wx.LEFT) bs.Add(hsClock, border=4, flag=wx.ALL) bs.Add(self.chipTimingOptions, border=border, flag=wx.ALL) #--------------------------------------------------------------------------------------------- self.rightPanel = wx.Panel(self.splitter) self.rightPanel.SetBackgroundColour(wx.Colour(255, 255, 255)) checklistTitle = wx.StaticText(self.rightPanel, label=_('Checklist:')) checklistTitle.SetFont( wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) self.checklist = Checklist.Checklist(self.rightPanel) hsSub = wx.BoxSizer(wx.VERTICAL) hsSub.Add(checklistTitle, 0, flag=wx.ALL, border=4) hsSub.Add(self.checklist, 1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=4) self.rightPanel.SetSizer(hsSub) #--------------------------------------------------------------------------------------------- self.splitter.SplitVertically(self.leftPanel, self.rightPanel) self.splitter.SetMinimumPaneSize(100) wx.CallAfter(self.refresh) wx.CallAfter(self.splitter.SetSashPosition, 650) wx.CallAfter(self.GetSizer().Layout) def setWrappedRaceInfo(self, event=None): wrapWidth = self.leftPanel.GetClientSize( )[0] - self.button.GetClientSize()[0] - 20 dc = wx.WindowDC(self.raceIntro) dc.SetFont(self.raceIntro.GetFont()) label = wordwrap(Model.race.getRaceIntro() if Model.race else u'', wrapWidth, dc) self.raceIntro.SetLabel(label) self.leftPanel.GetSizer().Layout() if event: event.Skip() def updateChipTimingOptions(self): if not Model.race: return iSelection = self.chipTimingOptions.GetSelection() race = Model.race race.resetStartClockOnFirstTag = bool( iSelection == self.iResetStartClockOnFirstTag) race.skipFirstTagRead = bool(iSelection == self.iSkipFirstTagRead) def updateClock(self): mainWin = Utils.getMainWin() return not mainWin or mainWin.isShowingPage(self) def onShowProperties(self, event): if not Model.race: Utils.MessageOK( self, _("You must have a valid race. Open or New a race first."), _("No Valid Race"), iconMask=wx.ICON_ERROR) return if not hasattr(self, 'propertiesDialog'): self.propertiesDialog = PropertiesDialog(self, showFileFields=False, updateProperties=True, size=(600, 400)) else: self.propertiesDialog.properties.refresh(forceUpdate=True) self.propertiesDialog.properties.setPage('raceOptionsProperties') if self.propertiesDialog.ShowModal() == wx.ID_OK: self.propertiesDialog.properties.doCommit() Utils.refresh() def onChipTimingOptions(self, event): if not Model.race: return self.updateChipTimingOptions() def onPress(self, event): if not Model.race: return with Model.LockRace() as race: running = race.isRunning() if running: self.onFinishRace(event) return self.updateChipTimingOptions() if getattr(Model.race, 'enableJChipIntegration', False): try: externalFields = race.excelLink.getFields() externalInfo = race.excelLink.read() except: externalFields = [] externalInfo = {} if not externalInfo: Utils.MessageOK( self, u'\n\n'.join([ _('Cannot Start. Excel Sheet read failure.'), _('The Excel file is either unconfigured or unreadable.' ) ]), _('Excel Sheet Read '), wx.ICON_ERROR) return try: i = next((i for i, field in enumerate(externalFields) if field.startswith('Tag'))) except StopIteration: Utils.MessageOK( self, u'\n\n'.join([ _('Cannot Start. Excel Sheet missing Tag column.'), _('The Excel file must contain a Tag column to use RFID.' ) ]), _('Excel Sheet missing Tag column'), wx.ICON_ERROR) return if self.startRaceTimeCheckBox.IsChecked(): self.onStartRaceTime(event) else: self.onStartRace(event) def onStartRace(self, event): if Model.race and Utils.MessageOKCancel(self, _('Start Race Now?\n\n'), _('Start Race')): StartRaceNow() def onStartRaceTime(self, event): if Model.race is None: return dlg = StartRaceAtTime(self) dlg.ShowModal() dlg.Destroy() def onFinishRace(self, event): if Model.race is None or not Utils.MessageOKCancel( self, _('Finish Race Now?'), _('Finish Race')): return with Model.LockRace() as race: race.finishRaceNow() if race.numLaps is None: race.numLaps = race.getMaxLap() SetNoDataDNS() Model.resetCache() Utils.writeRace() self.refresh() mainWin = Utils.getMainWin() if mainWin: mainWin.refresh() OutputStreamer.writeRaceFinish() OutputStreamer.StopStreamer() try: ChipReader.chipReaderCur.StopListener() except: pass if getattr(Model.race, 'ftpUploadDuringRace', False): realTimeFtpPublish.publishEntry(True) def commit(self): self.checklist.commit() def refresh(self): self.clock.Start() self.button.Enable(False) self.startRaceTimeCheckBox.Enable(False) self.settingsButton.Enable(False) self.button.SetLabel(StartText) self.button.SetForegroundColour(wx.Colour(100, 100, 100)) self.chipTimingOptions.SetSelection(0) self.chipTimingOptions.Enable(False) with Model.LockRace() as race: if race: self.settingsButton.Enable(True) # Adjust the chip recording options for TT. if getattr(race, 'isTimeTrial', False): race.resetStartClockOnFirstTag = False race.skipFirstTagRead = False if getattr(race, 'resetStartClockOnFirstTag', True): self.chipTimingOptions.SetSelection( self.iResetStartClockOnFirstTag) elif getattr(race, 'skipFirstTagRead', False): self.chipTimingOptions.SetSelection(self.iSkipFirstTagRead) if race.startTime is None: self.button.Enable(True) self.button.SetLabel(StartText) self.button.SetForegroundColour(wx.Colour(0, 128, 0)) self.startRaceTimeCheckBox.Enable(True) self.startRaceTimeCheckBox.Show(True) self.chipTimingOptions.Enable( getattr(race, 'enableJChipIntegration', False)) self.chipTimingOptions.Show( getattr(race, 'enableJChipIntegration', False)) elif race.isRunning(): self.button.Enable(True) self.button.SetLabel(FinishText) self.button.SetForegroundColour(wx.Colour(128, 0, 0)) self.startRaceTimeCheckBox.Enable(False) self.startRaceTimeCheckBox.Show(False) self.chipTimingOptions.Enable(False) self.chipTimingOptions.Show(False) # Adjust the time trial display options. if getattr(race, 'isTimeTrial', False): self.chipTimingOptions.Enable(False) self.chipTimingOptions.Show(False) self.GetSizer().Layout() self.setWrappedRaceInfo() self.checklist.refresh() mainWin = Utils.getMainWin() if mainWin is not None: mainWin.updateRaceClock()
class MainWin(wx.Frame): def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) dataDir = Utils.getHomeDir() configFileName = os.path.join(dataDir, 'CrossMgrAlien.cfg') self.config = wx.Config(appName="CrossMgrAlien", vendorName="SmartCyclingSolutions", localFilename=configFileName) ID_MENU_ADVANCECONFIG = wx.NewIdRef() ID_MENU_COPYLOGS = wx.NewIdRef() ID_MENU_AUTODETECT = wx.NewIdRef() self.menuBar = wx.MenuBar(wx.MB_DOCKABLE) if 'WXMAC' in wx.Platform: self.appleMenu = self.menuBar.OSXGetAppleMenu() self.appleMenu.SetTitle("CrossMgrAlien") self.appleMenu.Insert(0, wx.ID_ABOUT, "&About") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.editMenu = wx.Menu() self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_ADVANCECONFIG, "A&dvanced Configuration")) self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_COPYLOGS, "&Copy Logs to Clipboard")) self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_AUTODETECT, "&Autodetect Reader")) self.Bind(wx.EVT_MENU, self.doAdvanced, id=ID_MENU_ADVANCECONFIG) self.Bind(wx.EVT_MENU, self.doCopyToClipboard, id=ID_MENU_COPYLOGS) self.Bind(wx.EVT_MENU, self.doAutoDetect, id=ID_MENU_AUTODETECT) self.menuBar.Append(self.editMenu, "&Edit") else: self.fileMenu = wx.Menu() self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_ADVANCECONFIG, "A&dvanced Configuration")) self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_COPYLOGS, "&Copy Logs to Clipboard")) self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_AUTODETECT, "&Autodetect Reader")) self.fileMenu.Append(wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.doAdvanced, id=ID_MENU_ADVANCECONFIG) self.Bind(wx.EVT_MENU, self.doCopyToClipboard, id=ID_MENU_COPYLOGS) self.Bind(wx.EVT_MENU, self.doAutoDetect, id=ID_MENU_AUTODETECT) self.Bind(wx.EVT_MENU, self.onCloseWindow, id=wx.ID_EXIT) self.menuBar.Append(self.fileMenu, "&File") self.helpMenu = wx.Menu() self.helpMenu.Insert(0, wx.ID_ABOUT, "&About") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.menuBar.Append(self.helpMenu, "&Help") self.SetMenuBar(self.menuBar) self.SetBackgroundColour(wx.Colour(232, 232, 232)) self.LightGreen = wx.Colour(153, 255, 153) self.LightRed = wx.Colour(255, 153, 153) font = self.GetFont() bigFont = wx.Font(font.GetPointSize() * 1.5, font.GetFamily(), font.GetStyle(), wx.FONTWEIGHT_BOLD) titleFont = wx.Font(bigFont.GetPointSize() * 2.2, bigFont.GetFamily(), bigFont.GetStyle(), bigFont.GetWeight()) self.vbs = wx.BoxSizer(wx.VERTICAL) bs = wx.BoxSizer(wx.HORIZONTAL) self.reset = RoundButton(self, wx.ID_ANY, 'Reset', size=(80, 80)) self.reset.SetBackgroundColour(wx.WHITE) self.reset.SetForegroundColour(wx.Colour(0, 128, 128)) self.reset.SetFontToFitLabel( ) # Use the button's default font, but change the font size to fit the label. self.reset.Bind(wx.EVT_BUTTON, self.doReset) self.reset.Refresh() bs.Add(self.reset, border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(titleFont, wx.StaticText(self, wx.ID_ANY, 'CrossMgrAlien')), border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.AddStretchSpacer() bitmap = wx.Bitmap(clipboard_xpm) self.copyToClipboard = wx.BitmapButton(self, wx.ID_ANY, bitmap) self.copyToClipboard.SetToolTip( wx.ToolTip('Copy Configuration and Logs to Clipboard...')) self.copyToClipboard.Bind(wx.EVT_BUTTON, self.doCopyToClipboard) bs.Add(self.copyToClipboard, border=32, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.tStart = datetime.datetime.now() bs.Add(setFont( bigFont, wx.StaticText( self, wx.ID_ANY, 'Last Reset: {}'.format(self.tStart.strftime('%H:%M:%S')))), border=10, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.runningTime = setFont(bigFont, wx.StaticText(self, wx.ID_ANY, '00:00:00')) bs.Add(self.runningTime, border=20, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(bigFont, wx.StaticText(self, wx.ID_ANY, ' / ')), flag=wx.ALIGN_CENTER_VERTICAL) self.time = setFont(bigFont, wx.StaticText(self, wx.ID_ANY, '00:00:00')) bs.Add(self.time, flag=wx.ALIGN_CENTER_VERTICAL) self.vbs.Add(bs, flag=wx.ALL | wx.EXPAND, border=4) fgs = wx.FlexGridSizer(rows=0, cols=2, vgap=4, hgap=4) fgs.AddGrowableRow(1) fgs.AddGrowableCol(0) fgs.AddGrowableCol(1) fgs.SetFlexibleDirection(wx.BOTH) self.vbs.Add(fgs, flag=wx.EXPAND, proportion=5) #------------------------------------------------------------------------------------------------ # Alien configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) iRow = 0 hs = wx.BoxSizer(wx.HORIZONTAL) hs.Add(setFont(bigFont, wx.StaticText(self, wx.ID_ANY, 'Alien Configuration:')), flag=wx.ALIGN_CENTER_VERTICAL) self.autoDetectButton = wx.Button(self, wx.ID_ANY, 'Auto Detect') self.autoDetectButton.Bind(wx.EVT_BUTTON, self.doAutoDetect) hs.Add(self.autoDetectButton, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=6) self.advancedButton = wx.Button(self, wx.ID_ANY, 'Advanced...') self.advancedButton.Bind(wx.EVT_BUTTON, self.doAdvanced) hs.Add(self.advancedButton, flag=wx.LEFT, border=6) gbs.Add(hs, pos=(iRow, 0), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, wx.ID_ANY, 'Antennas:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) gs = wx.GridSizer(rows=0, cols=4, hgap=2, vgap=2) self.antennas = [] for i in range(4): gs.Add(wx.StaticText(self, wx.ID_ANY, '{}'.format(i)), flag=wx.ALIGN_CENTER) for i in range(4): cb = wx.CheckBox(self, wx.ID_ANY, '') if i < 2: cb.SetValue(True) cb.Bind(wx.EVT_CHECKBOX, lambda x: self.getAntennaStr()) gs.Add(cb, flag=wx.ALIGN_CENTER) self.antennas.append(cb) gbs.Add(gs, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) iRow += 1 gbs.Add(wx.StaticText(self, wx.ID_ANY, 'Notify Address:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) ips = Utils.GetAllIps() self.notifyHost = wx.Choice(self, choices=ips) hb.Add(self.notifyHost) hb.Add(wx.StaticText(self, label=' : {}'.format(NotifyPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1)) iRow += 1 self.listenForHeartbeat = wx.CheckBox( self, label='Listen for Alien Heartbeat on Port: {}'.format( HeartbeatPort), style=wx.ALIGN_LEFT) self.listenForHeartbeat.SetValue(True) gbs.Add(self.listenForHeartbeat, pos=(iRow, 0), span=(1, 2)) iRow += 1 gbs.Add(wx.StaticText(self, label='Alien Cmd Address:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.cmdHost = IpAddrCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_TAB) hb.Add(self.cmdHost) hb.Add(wx.StaticText(self, label=' : '), flag=wx.ALIGN_CENTER_VERTICAL) self.cmdPort = intctrl.IntCtrl(self, size=(50, -1), min=0, max=999999) hb.Add(self.cmdPort, flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, label='Backup File:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.backupFile = wx.StaticText(self, label='') gbs.Add(self.backupFile, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # CrossMgr configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) gbs.Add(setFont(bigFont, wx.StaticText(self, label='CrossMgr Configuration:')), pos=(0, 0), span=(1, 2), flag=wx.ALIGN_LEFT) gbs.Add(wx.StaticText(self, label='CrossMgr Address:'), pos=(1, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.crossMgrHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.crossMgrHost, flag=wx.ALIGN_LEFT) hb.Add(wx.StaticText(self, label=' : 53135'), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(1, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # Add messages # self.alienMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.alienMessagesText, flag=wx.EXPAND, proportion=2) self.alienMessages = MessageManager(self.alienMessagesText) self.crossMgrMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.crossMgrMessagesText, flag=wx.EXPAND, proportion=2) self.crossMgrMessages = MessageManager(self.crossMgrMessagesText) self.fgs = fgs #------------------------------------------------------------------------------------------------ # Create a timer to update the messages. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.updateMessages) self.timer.Start(1000, False) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizer(self.vbs) self.start() def OnAboutBox(self, e): description = """CrossMgrAlien is an Impinj interface to CrossMgr """ licence = """CrossMgrAlien is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. CrossMgrImpinj is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with File Hunter; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA""" info = wx.adv.AboutDialogInfo() crossMgrPng = Utils.getImageFolder() + '/CrossMgrAlien.png' info.SetIcon(wx.Icon(crossMgrPng, wx.BITMAP_TYPE_PNG)) info.SetName('CrossMgrAlien') info.SetVersion(AppVerName.split(' ')[1]) info.SetDescription(description) info.SetCopyright('(C) 2020 Edward Sitarski') info.SetWebSite('http://www.sites.google.com/site/crossmgrsoftware/') info.SetLicence(licence) wx.adv.AboutBox(info, self) def start(self): self.dataQ = Queue() self.messageQ = Queue() self.shutdownQ = Queue( ) # Queue to tell the Alien monitor to shut down. self.alienProcess = Process( name='AlienProcess', target=AlienServer, args=(self.dataQ, self.messageQ, self.shutdownQ, self.getNotifyHost(), NotifyPort, HeartbeatPort, self.getAntennaStr(), self.listenForHeartbeat.GetValue(), self.cmdHost.GetAddress(), self.cmdPort.GetValue())) self.alienProcess.daemon = True self.crossMgrProcess = Process(name='CrossMgrProcess', target=CrossMgrServer, args=(self.dataQ, self.messageQ, self.shutdownQ, self.getCrossMgrHost(), CrossMgrPort)) self.crossMgrProcess.daemon = True self.alienProcess.start() self.crossMgrProcess.start() def doAdvanced(self, event): dlg = AdvancedSetup(self) dlg.ShowModal() dlg.Destroy() self.writeOptions() def shutdown(self): self.alienProcess = None self.crossMgrProcess = None self.messageQ = None self.dataQ = None self.shutdownQ = None def gracefulShutdown(self): # Shutdown the CrossMgr process by sending it a shutdown command. if self.shutdownQ: self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') if self.dataQ: self.dataQ.put('shutdown') if self.crossMgrProcess: self.crossMgrProcess.join() if self.alienProcess: self.alienProcess.join() self.crossMgrProcess = None self.alienProcess = None def doReset(self, event, confirm=True): if confirm: dlg = wx.MessageDialog(self, 'Reset CrossMgrAlien Adapter?', 'Confirm Reset', wx.OK | wx.CANCEL | wx.ICON_WARNING) ret = dlg.ShowModal() dlg.Destroy() if ret != wx.ID_OK: return self.reset.Enable( False) # Prevent multiple clicks while shutting down. self.writeOptions() self.gracefulShutdown() self.alienMessages.clear() self.crossMgrMessages.clear() self.shutdown() self.reset.Enable(True) wx.CallAfter(self.start) def doAutoDetect(self, event): wx.BeginBusyCursor() self.gracefulShutdown() self.shutdown() alienHost, crossmgrHost = AutoDetect() wx.EndBusyCursor() if alienHost and crossmgrHost: self.setNotifyHost( crossmgrHost ) # Assumes CrossMgr is on the same computer as CrossMgrAlien. self.cmdHost.SetValue(alienHost) self.cmdPort.SetValue(str(DefaultAlienCmdPort)) self.crossMgrHost.SetValue(crossmgrHost) self.listenForHeartbeat.SetValue(False) else: dlg = wx.MessageDialog( self, 'Auto Detect Failed.\nCheck that reader has power and is connected to the router.', 'Auto Detect Failed', wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() self.doReset(event, False) def onCloseWindow(self, event): wx.Exit() def doCopyToClipboard(self, event): cc = [ 'Configuration: CrossMgrAlien', ' NotifyHost: {}'.format(self.getNotifyHost()), ' NotifyPort: {}'.format(NotifyPort), ' RunningTime: {}'.format(self.runningTime.GetLabel()), ' Time: {}'.format(self.time.GetLabel()), ' BackupFile: {}'.format(self.backupFile.GetLabel()), '', 'Configuration: Alien:', ' ListenForAlienHeartbeat: {}'.format( 'True' if self.listenForHeartbeat.GetValue() else 'False'), ' Antennas: {}'.format(self.getAntennaStr()), ' HeartbeatPort: {}'.format(HeartbeatPort), ' AlienCmdHost: {}'.format(self.cmdHost.GetAddress()), ' AlienCmdPort: {}'.format(self.cmdPort.GetValue()), '', ' RepeatSeconds: {}'.format(Alien.RepeatSeconds), '', 'Configuration: CrossMgr', ' CrossMgrHost: {}'.format(self.getCrossMgrHost()), ' CrossMgrPort: {}'.format(CrossMgrPort), ] cc.append('\nLog: Alien') log = self.alienMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: CrossMgr') log = self.crossMgrMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: Application\n') try: with open(redirectFileName, 'r') as fp: for line in fp: cc.append(line) except: pass if wx.TheClipboard.Open(): do = wx.TextDataObject() do.SetText('\n'.join(cc)) wx.TheClipboard.SetData(do) wx.TheClipboard.Close() dlg = wx.MessageDialog( self, 'Configuration and Logs copied to the Clipboard.', 'Copy to Clipboard Succeeded', wx.OK | wx.ICON_INFORMATION) ret = dlg.ShowModal() dlg.Destroy() else: # oops... something went wrong! wx.MessageBox("Unable to open the clipboard", "Error") def getNotifyHost(self): s = self.notifyHost.GetSelection() return self.notifyHost.GetString(s) if s != wx.NOT_FOUND else None def setNotifyHost(self, notifyHost): for i, s in enumerate(self.notifyHost.GetItems()): if s == notifyHost: self.notifyHost.SetSelection(i) return self.notifyHost.SetSelection(0) def getCrossMgrHost(self): return self.crossMgrHost.GetAddress() def getAntennaStr(self): s = [] for i in range(4): if self.antennas[i].GetValue(): s.append('%d' % i) if not s: # Ensure that at least one antenna is selected. self.antennas[0].SetValue(True) s.append('0') return ' '.join(s) def setAntennaStr(self, s): antennas = set(int(a) for a in s.split()) for i in range(4): self.antennas[i].SetValue(i in antennas) def writeOptions(self): self.config.Write('CrossMgrHost', self.getCrossMgrHost()) self.config.Write( 'ListenForAlienHeartbeat', 'True' if self.listenForHeartbeat.GetValue() else 'False') self.config.Write('AlienCmdAddr', self.cmdHost.GetAddress()) self.config.Write('AlienCmdPort', str(self.cmdPort.GetValue())) self.config.Write('Antennas', self.getAntennaStr()) self.config.Write('RepeatSeconds', '{}'.format(Alien.RepeatSeconds)) self.config.Write('PlaySounds', '{}'.format(Utils.playBell)) s = self.notifyHost.GetSelection() if s != wx.NOT_FOUND: self.config.Write('NotifyHost', self.notifyHost.GetString(s)) self.config.Flush() def readOptions(self): self.crossMgrHost.SetValue( self.config.Read('CrossMgrHost', Utils.DEFAULT_HOST)) self.listenForHeartbeat.SetValue( self.config.Read('ListenForAlienHeartbeat', 'True').upper()[:1] == 'T') Utils.playBell = (self.config.Read('PlaySounds', 'True').upper()[:1] == 'T') self.cmdHost.SetValue(self.config.Read('AlienCmdAddr', '0.0.0.0')) self.cmdPort.SetValue(int(self.config.Read('AlienCmdPort', '0'))) self.setAntennaStr(self.config.Read('Antennas', '0 1')) Alien.RepeatSeconds = int( self.config.Read('RepeatSeconds', '{}'.format(Alien.RepeatSeconds))) notifyHost = self.config.Read('NotifyHost', Utils.DEFAULT_HOST) self.setNotifyHost(notifyHost) def updateMessages(self, event): tNow = datetime.datetime.now() running = int((tNow - self.tStart).total_seconds()) self.runningTime.SetLabel('{:02d}:{:02d}:{:02d}'.format( running // (60 * 60), (running // 60) % 60, running % 60)) self.time.SetLabel(tNow.strftime('%H:%M:%S')) if not self.messageQ: return while 1: try: d = self.messageQ.get(False) except Empty: break message = ' '.join(str(x) for x in d[1:]) if d[0] == 'Alien': if 'state' in d: self.alienMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.alienMessages.write(message) elif d[0] == 'Alien2JChip': if 'state' in d: self.crossMgrMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.crossMgrMessages.write(message) elif d[0] == 'CmdHost': cmdHost, cmdPort = d[1].split(':') self.cmdHost.SetValue(cmdHost) self.cmdPort.SetValue(int(cmdPort)) elif d[0] == 'BackupFile': self.backupFile.SetLabel(d[1])