# HG changeset patch # User Edouard Tisserant # Date 1423431557 -3600 # Node ID 826730e60407930a08c7c26c072df6b1e5d84cba # Parent e8daabf2c438b83da8dfad1400eda28b74a18653 Added auto-reconnect for runtime. Fixed Beremiz closing problem caused by remaining twisted reactor thread in IDE. diff -r e8daabf2c438 -r 826730e60407 Beremiz.py --- a/Beremiz.py Sun Feb 08 16:50:54 2015 +0100 +++ b/Beremiz.py Sun Feb 08 22:39:17 2015 +0100 @@ -36,6 +36,7 @@ CWD = os.path.split(os.path.realpath(__file__))[0] + def Bpath(*args): return os.path.join(CWD,*args) @@ -634,6 +635,14 @@ else: return IDEFrame.LoadTab(self, notebook, page_infos) + # Strange hack required by WAMP connector, using twisted. + # Twisted reactor needs to be stopped only before quit, + # since it cannot be restarted + ToDoBeforeQuit = [] + def AddToDoBeforeQuit(self, Thing): + self.ToDoBeforeQuit.append(Thing) + print self.ToDoBeforeQuit + def OnCloseFrame(self, event): for evt_type in [wx.EVT_SET_FOCUS, wx.EVT_KILL_FOCUS, @@ -646,6 +655,10 @@ self.SaveLastState() + for Thing in self.ToDoBeforeQuit : + Thing() + self.ToDoBeforeQuit = [] + event.Skip() else: event.Veto() diff -r e8daabf2c438 -r 826730e60407 connectors/PYRO/__init__.py --- a/connectors/PYRO/__init__.py Sun Feb 08 16:50:54 2015 +0100 +++ b/connectors/PYRO/__init__.py Sun Feb 08 22:39:17 2015 +0100 @@ -94,7 +94,7 @@ return None - class PyroProxyProxy: + class PyroProxyProxy(object): """ A proxy proxy class to handle Beremiz Pyro interface specific behavior. And to put pyro exception catcher in between caller and pyro proxy diff -r e8daabf2c438 -r 826730e60407 connectors/WAMP/__init__.py --- a/connectors/WAMP/__init__.py Sun Feb 08 16:50:54 2015 +0100 +++ b/connectors/WAMP/__init__.py Sun Feb 08 22:39:17 2015 +0100 @@ -19,7 +19,7 @@ #License along with this library; if not, write to the Free Software #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -import sys, traceback +import sys, traceback, atexit #from twisted.python import log from twisted.internet import reactor, threads from autobahn.twisted import wamp @@ -30,18 +30,18 @@ from threading import Thread, Event _WampSession = None -_ReactorThread = None +_WampConnection = None _WampSessionEvent = Event() class WampSession(wamp.ApplicationSession): def onJoin(self, details): - global _WampSession + global _WampSession, _WampSessionEvent _WampSession = self _WampSessionEvent.set() print 'WAMP session joined for :', self.config.extra["ID"] def onLeave(self, details): - global _WampSession + global _WampSession, _WampSessionEvent _WampSessionEvent.clear() _WampSession = None print 'WAMP session left' @@ -56,8 +56,6 @@ WAMP://127.0.0.1:12345/path#realm#ID WAMPS://127.0.0.1:12345/path#realm#ID """ - global _WampSession, _ReactorThread, _WampSessionEvent - servicetype, location = uri.split("://") urlpath, realm, ID = location.split('#') urlprefix = {"WAMP":"ws", @@ -90,8 +88,16 @@ confnodesroot.logger.write(_("WAMP connecting to URL : %s\n")%url) return conn + AddToDoBeforeQuit = confnodesroot.AppFrame.AddToDoBeforeQuit + def ThreadProc(): + global _WampConnection + _WampConnection = RegisterWampClient() + AddToDoBeforeQuit(reactor.stop) + reactor.run(installSignalHandlers=False) + def WampSessionProcMapper(funcname): def catcher_func(*args,**kwargs): + global _WampSession if _WampSession is not None : try: return threads.blockingCallFromThread( @@ -110,21 +116,21 @@ class WampPLCObjectProxy(object): def __init__(self): + global _WampSessionEvent, _WampConnection if not reactor.running: - def ThreadProc(): - self.connection = RegisterWampClient() - reactor.run(installSignalHandlers=False) Thread(target=ThreadProc).start() else: - self.connection = threads.blockingCallFromThread( + _WampConnection = threads.blockingCallFromThread( reactor, RegisterWampClient) if not _WampSessionEvent.wait(5): - self.connection.stopConnecting() + _WampConnection = stopConnecting() raise Exception, _("WAMP connection timeout") def __del__(self): - self.connection.disconnect() - #reactor.Stop() + global _WampConnection + _WampConnection.disconnect() + # + # reactor.stop() def __getattr__(self, attrName): member = self.__dict__.get(attrName, None) diff -r e8daabf2c438 -r 826730e60407 controls/LogViewer.py --- a/controls/LogViewer.py Sun Feb 08 16:50:54 2015 +0100 +++ b/controls/LogViewer.py Sun Feb 08 22:39:17 2015 +0100 @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor -#based on the plcopen standard. +#based on the plcopen standard. # #Copyright (C) 2013: Edouard TISSERANT and Laurent BESSARD # @@ -32,6 +32,7 @@ from editors.DebugViewer import DebugViewer, REFRESH_PERIOD from targets.typemapping import LogLevelsCount, LogLevels from util.BitmapLibrary import GetBitmap +from weakref import proxy THUMB_SIZE_RATIO = 1. / 8. @@ -46,7 +47,7 @@ wx.Point(xoffset + width - 1, yoffset - height + 1)] class LogScrollBar(wx.Panel): - + def __init__(self, parent, size): wx.Panel.__init__(self, parent, size=size) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) @@ -55,14 +56,14 @@ self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_SIZE, self.OnResize) - + self.ThumbPosition = 0. # -1 <= ThumbPosition <= 1 self.ThumbScrollingStartPos = None - + def GetRangeRect(self): width, height = self.GetClientSize() return wx.Rect(0, width, width, height - 2 * width) - + def GetThumbRect(self): width, height = self.GetClientSize() range_rect = self.GetRangeRect() @@ -72,7 +73,7 @@ thumb_start = int(thumb_center_position - thumb_size / 2.) thumb_end = int(thumb_center_position + thumb_size / 2.) return wx.Rect(0, range_rect.y + thumb_start, width, thumb_end - thumb_start) - + def RefreshThumbPosition(self, thumb_position=None): if thumb_position is None: thumb_position = self.ThumbPosition @@ -84,7 +85,7 @@ self.ThumbPosition = thumb_position self.Parent.SetScrollSpeed(self.ThumbPosition) self.Refresh() - + def OnLeftDown(self, event): self.CaptureMouse() posx, posy = event.GetPosition() @@ -103,14 +104,14 @@ elif posy > height - width: self.Parent.ScrollMessagePanelByPage(-1) event.Skip() - + def OnLeftUp(self, event): self.ThumbScrollingStartPos = None self.RefreshThumbPosition(0.) if self.HasCapture(): self.ReleaseMouse() event.Skip() - + def OnMotion(self, event): if event.Dragging() and self.ThumbScrollingStartPos is not None: posx, posy = event.GetPosition() @@ -121,32 +122,32 @@ self.RefreshThumbPosition( max(-1., min((posy - self.ThumbScrollingStartPos.y) * 2. / thumb_range, 1.))) event.Skip() - + def OnResize(self, event): self.Refresh() event.Skip() - + def OnEraseBackground(self, event): pass - + def OnPaint(self, event): dc = wx.BufferedPaintDC(self) dc.Clear() dc.BeginDrawing() - + gc = wx.GCDC(dc) - + width, height = self.GetClientSize() - + gc.SetPen(wx.Pen(wx.NamedColour("GREY"), 3)) gc.SetBrush(wx.GREY_BRUSH) - + gc.DrawLines(ArrowPoints(wx.TOP, width * 0.75, width * 0.5, 2, (width + height) / 4 - 3)) gc.DrawLines(ArrowPoints(wx.TOP, width * 0.75, width * 0.5, 2, (width + height) / 4 + 3)) - + gc.DrawLines(ArrowPoints(wx.BOTTOM, width * 0.75, width * 0.5, 2, (height * 3 - width) / 4 + 3)) gc.DrawLines(ArrowPoints(wx.BOTTOM, width * 0.75, width * 0.5, 2, (height * 3 - width) / 4 - 3)) - + thumb_rect = self.GetThumbRect() exclusion_rect = wx.Rect(thumb_rect.x, thumb_rect.y, thumb_rect.width, thumb_rect.height) @@ -158,71 +159,71 @@ colour = wx.NamedColour("LIGHT GREY") gc.SetPen(wx.Pen(colour)) gc.SetBrush(wx.Brush(colour)) - - gc.DrawRectangle(exclusion_rect.x, exclusion_rect.y, + + gc.DrawRectangle(exclusion_rect.x, exclusion_rect.y, exclusion_rect.width, exclusion_rect.height) - + gc.SetPen(wx.GREY_PEN) gc.SetBrush(wx.GREY_BRUSH) - + gc.DrawPolygon(ArrowPoints(wx.TOP, width, width, 0, 0)) - + gc.DrawPolygon(ArrowPoints(wx.BOTTOM, width, width, 0, height)) - - gc.DrawRectangle(thumb_rect.x, thumb_rect.y, + + gc.DrawRectangle(thumb_rect.x, thumb_rect.y, thumb_rect.width, thumb_rect.height) - + dc.EndDrawing() event.Skip() BUTTON_SIZE = (30, 15) class LogButton(): - + def __init__(self, label, callback): self.Position = wx.Point(0, 0) self.Size = wx.Size(*BUTTON_SIZE) self.Label = label self.Shown = True self.Callback = callback - + def __del__(self): self.callback = None - + def GetSize(self): return self.Size - + def SetPosition(self, x, y): self.Position = wx.Point(x, y) - + def HitTest(self, x, y): - rect = wx.Rect(self.Position.x, self.Position.y, + rect = wx.Rect(self.Position.x, self.Position.y, self.Size.width, self.Size.height) if rect.InsideXY(x, y): return True return False - + def ProcessCallback(self): if self.Callback is not None: wx.CallAfter(self.Callback) - + def Draw(self, dc): dc.SetPen(wx.TRANSPARENT_PEN) dc.SetBrush(wx.Brush(wx.NamedColour("LIGHT GREY"))) - - dc.DrawRectangle(self.Position.x, self.Position.y, + + dc.DrawRectangle(self.Position.x, self.Position.y, self.Size.width, self.Size.height) - + w, h = dc.GetTextExtent(self.Label) - dc.DrawText(self.Label, - self.Position.x + (self.Size.width - w) / 2, + dc.DrawText(self.Label, + self.Position.x + (self.Size.width - w) / 2, self.Position.y + (self.Size.height - h) / 2) DATE_INFO_SIZE = 10 MESSAGE_INFO_SIZE = 18 class LogMessage: - + def __init__(self, tv_sec, tv_nsec, level, level_bitmap, msg): self.Date = datetime.utcfromtimestamp(tv_sec) self.Seconds = self.Date.second + tv_nsec * 1e-9 @@ -232,12 +233,12 @@ self.LevelBitmap = level_bitmap self.Message = msg self.DrawDate = True - + def __cmp__(self, other): if self.Date == other.Date: return cmp(self.Seconds, other.Seconds) return cmp(self.Date, other.Date) - + def GetFullText(self): date = self.Date.replace(second=int(self.Seconds)) nsec = (self.Seconds % 1.) * 1e9 @@ -245,25 +246,25 @@ LogLevels[self.Level], str(date), nsec, self.Message) - + def Draw(self, dc, offset, width, draw_date): if draw_date: datetime_text = self.Date.strftime("%d/%m/%y %H:%M") dw, dh = dc.GetTextExtent(datetime_text) dc.DrawText(datetime_text, (width - dw) / 2, offset + (DATE_INFO_SIZE - dh) / 2) offset += DATE_INFO_SIZE - + seconds_text = "%12.9f" % self.Seconds sw, sh = dc.GetTextExtent(seconds_text) dc.DrawText(seconds_text, 5, offset + (MESSAGE_INFO_SIZE - sh) / 2) - + bw, bh = self.LevelBitmap.GetWidth(), self.LevelBitmap.GetHeight() dc.DrawBitmap(self.LevelBitmap, 10 + sw, offset + (MESSAGE_INFO_SIZE - bh) / 2) - + text = self.Message.replace("\n", " ") mw, mh = dc.GetTextExtent(text) dc.DrawText(text, 15 + sw + bw, offset + (MESSAGE_INFO_SIZE - mh) / 2) - + def GetHeight(self, draw_date): if draw_date: return DATE_INFO_SIZE + MESSAGE_INFO_SIZE @@ -280,18 +281,18 @@ (_("1s"), SECOND)] class LogViewer(DebugViewer, wx.Panel): - + def __init__(self, parent, window): wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) DebugViewer.__init__(self, None, False, False) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + filter_sizer = wx.BoxSizer(wx.HORIZONTAL) main_sizer.AddSizer(filter_sizer, border=5, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW) - + self.MessageFilter = wx.ComboBox(self, style=wx.CB_READONLY) self.MessageFilter.Append(_("All")) levels = LogLevels[:3] @@ -300,28 +301,28 @@ self.MessageFilter.Append(_(level)) self.Bind(wx.EVT_COMBOBOX, self.OnMessageFilterChanged, self.MessageFilter) filter_sizer.AddWindow(self.MessageFilter, 1, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) - + self.SearchMessage = wx.SearchCtrl(self, style=wx.TE_PROCESS_ENTER) self.SearchMessage.ShowSearchButton(True) self.SearchMessage.ShowCancelButton(True) self.Bind(wx.EVT_TEXT_ENTER, self.OnSearchMessageChanged, self.SearchMessage) - self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, + self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, self.OnSearchMessageSearchButtonClick, self.SearchMessage) - self.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN, + self.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN, self.OnSearchMessageCancelButtonClick, self.SearchMessage) filter_sizer.AddWindow(self.SearchMessage, 3, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) - - self.CleanButton = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap("Clean"), + + self.CleanButton = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap("Clean"), size=wx.Size(28, 28), style=wx.NO_BORDER) self.CleanButton.SetToolTipString(_("Clean log messages")) self.Bind(wx.EVT_BUTTON, self.OnCleanButton, self.CleanButton) filter_sizer.AddWindow(self.CleanButton) - + message_panel_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) message_panel_sizer.AddGrowableCol(0) message_panel_sizer.AddGrowableRow(0) main_sizer.AddSizer(message_panel_sizer, border=5, flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW) - + self.MessagePanel = wx.Panel(self) if wx.Platform == '__WXMSW__': self.Font = wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName='Courier New') @@ -337,45 +338,45 @@ self.MessagePanel.Bind(wx.EVT_PAINT, self.OnMessagePanelPaint) self.MessagePanel.Bind(wx.EVT_SIZE, self.OnMessagePanelResize) message_panel_sizer.AddWindow(self.MessagePanel, flag=wx.GROW) - + self.MessageScrollBar = LogScrollBar(self, wx.Size(16, -1)) message_panel_sizer.AddWindow(self.MessageScrollBar, flag=wx.GROW) - + self.SetSizer(main_sizer) - + self.LeftButtons = [] - for label, callback in [("+" + text, self.GenerateOnDurationButton(duration)) + for label, callback in [("+" + text, self.GenerateOnDurationButton(duration)) for text, duration in CHANGE_TIMESTAMP_BUTTONS]: self.LeftButtons.append(LogButton(label, callback)) - + self.RightButtons = [] - for label, callback in [("-" + text, self.GenerateOnDurationButton(-duration)) + for label, callback in [("-" + text, self.GenerateOnDurationButton(-duration)) for text, duration in CHANGE_TIMESTAMP_BUTTONS]: self.RightButtons.append(LogButton(label, callback)) - + self.MessageFilter.SetSelection(0) self.LogSource = None self.ResetLogMessages() self.ParentWindow = window - + self.LevelIcons = [GetBitmap("LOG_" + level) for level in LogLevels] self.LevelFilters = [range(i) for i in xrange(4, 0, -1)] self.CurrentFilter = self.LevelFilters[0] self.CurrentSearchValue = "" - + self.ScrollSpeed = 0. self.LastStartTime = None self.ScrollTimer = wx.Timer(self, -1) self.Bind(wx.EVT_TIMER, self.OnScrollTimer, self.ScrollTimer) - + self.LastMousePos = None self.MessageToolTip = None self.MessageToolTipTimer = wx.Timer(self, -1) self.Bind(wx.EVT_TIMER, self.OnMessageToolTipTimer, self.MessageToolTipTimer) - + def __del__(self): self.ScrollTimer.Stop() - + def ResetLogMessages(self): self.previous_log_count = [None]*LogLevelsCount self.OldestMessages = [] @@ -383,14 +384,14 @@ self.LogMessagesTimestamp = numpy.array([]) self.CurrentMessage = None self.HasNewData = False - + def SetLogSource(self, log_source): - self.LogSource = log_source + self.LogSource = proxy(log_source) if log_source else None self.CleanButton.Enable(self.LogSource is not None) if log_source is not None: self.ResetLogMessages() self.RefreshView() - + def GetLogMessageFromSource(self, msgidx, level): if self.LogSource is not None: answer = self.LogSource.GetLogMessage(level, msgidx) @@ -398,7 +399,7 @@ msg, tick, tv_sec, tv_nsec = answer return LogMessage(tv_sec, tv_nsec, level, self.LevelIcons[level], msg) return None - + def SetLogCounters(self, log_count): new_messages = [] for level, count, prev in zip(xrange(LogLevelsCount), log_count, self.previous_log_count): @@ -441,12 +442,12 @@ self.MessageToolTipTimer.Stop() self.ParentWindow.SelectTab(self) self.NewDataAvailable(None) - + def FilterLogMessage(self, message, timestamp=None): - return (message.Level in self.CurrentFilter and + return (message.Level in self.CurrentFilter and message.Message.find(self.CurrentSearchValue) != -1 and (timestamp is None or message.Timestamp < timestamp)) - + def GetMessageByTimestamp(self, timestamp): if self.CurrentMessage is not None: msgidx = numpy.argmin(abs(self.LogMessagesTimestamp - timestamp)) @@ -455,7 +456,7 @@ return self.GetPreviousMessage(msgidx, timestamp) return message, msgidx return None, None - + def GetNextMessage(self, msgidx): while msgidx < len(self.LogMessages) - 1: message = self.LogMessages[msgidx + 1] @@ -463,7 +464,7 @@ return message, msgidx + 1 msgidx += 1 return None, None - + def GetPreviousMessage(self, msgidx, timestamp=None): message = None while 0 < msgidx < len(self.LogMessages): @@ -490,7 +491,7 @@ self.OldestMessages[level] = (-1, None) if message is not None: message_idx = 0 - while (message_idx < len(self.LogMessages) and + while (message_idx < len(self.LogMessages) and self.LogMessages[message_idx] < message): message_idx += 1 if len(self.LogMessages) > 0: @@ -499,8 +500,8 @@ current_message = message self.LogMessages.insert(message_idx, message) self.LogMessagesTimestamp = numpy.insert( - self.LogMessagesTimestamp, - [message_idx], + self.LogMessagesTimestamp, + [message_idx], [message.Timestamp]) self.CurrentMessage = self.LogMessages.index(current_message) if message_idx == 0 and self.FilterLogMessage(message, timestamp): @@ -509,27 +510,27 @@ if msg is not None and (message is None or msg > message): message = msg return None, None - + def RefreshNewData(self, *args, **kwargs): if self.HasNewData: self.HasNewData = False self.RefreshView() DebugViewer.RefreshNewData(self, *args, **kwargs) - + def RefreshView(self): width, height = self.MessagePanel.GetClientSize() bitmap = wx.EmptyBitmap(width, height) dc = wx.BufferedDC(wx.ClientDC(self.MessagePanel), bitmap) dc.Clear() dc.BeginDrawing() - + if self.CurrentMessage is not None: - + dc.SetFont(self.Font) - + for button in self.LeftButtons + self.RightButtons: button.Draw(dc) - + message_idx = self.CurrentMessage message = self.LogMessages[message_idx] draw_date = True @@ -537,23 +538,23 @@ while offset < height and message is not None: message.Draw(dc, offset, width, draw_date) offset += message.GetHeight(draw_date) - + previous_message, message_idx = self.GetPreviousMessage(message_idx) if previous_message is not None: draw_date = message.Date != previous_message.Date message = previous_message - + dc.EndDrawing() - + self.MessageScrollBar.RefreshThumbPosition() - + def IsMessagePanelTop(self, message_idx=None): if message_idx is None: message_idx = self.CurrentMessage if message_idx is not None: return self.GetNextMessage(message_idx)[0] is None return True - + def IsMessagePanelBottom(self, message_idx=None): if message_idx is None: message_idx = self.CurrentMessage @@ -570,7 +571,7 @@ message = previous_message return offset < height return True - + def ScrollMessagePanel(self, scroll): if self.CurrentMessage is not None: message = self.LogMessages[self.CurrentMessage] @@ -585,13 +586,13 @@ self.CurrentMessage = msgidx scroll += 1 self.RefreshView() - + def ScrollMessagePanelByPage(self, page): if self.CurrentMessage is not None: width, height = self.MessagePanel.GetClientSize() message_per_page = max(1, (height - DATE_INFO_SIZE) / MESSAGE_INFO_SIZE - 1) self.ScrollMessagePanel(page * message_per_page) - + def ScrollMessagePanelByTimestamp(self, seconds): if self.CurrentMessage is not None: current_message = self.LogMessages[self.CurrentMessage] @@ -603,7 +604,7 @@ msgidx += 1 self.CurrentMessage = msgidx self.RefreshView() - + def ResetMessagePanel(self): if len(self.LogMessages) > 0: self.CurrentMessage = len(self.LogMessages) - 1 @@ -611,45 +612,45 @@ while message is not None and not self.FilterLogMessage(message): message, self.CurrentMessage = self.GetPreviousMessage(self.CurrentMessage) self.RefreshView() - + def OnMessageFilterChanged(self, event): self.CurrentFilter = self.LevelFilters[self.MessageFilter.GetSelection()] self.ResetMessagePanel() event.Skip() - + def OnSearchMessageChanged(self, event): self.CurrentSearchValue = self.SearchMessage.GetValue() self.ResetMessagePanel() event.Skip() - + def OnSearchMessageSearchButtonClick(self, event): self.CurrentSearchValue = self.SearchMessage.GetValue() self.ResetMessagePanel() event.Skip() - + def OnSearchMessageCancelButtonClick(self, event): self.CurrentSearchValue = "" self.SearchMessage.SetValue("") self.ResetMessagePanel() event.Skip() - + def OnCleanButton(self, event): if self.LogSource is not None: self.LogSource.ResetLogCount() self.ResetLogMessages() self.RefreshView() event.Skip() - + def GenerateOnDurationButton(self, duration): def OnDurationButton(): self.ScrollMessagePanelByTimestamp(duration) return OnDurationButton - + def GetCopyMessageToClipboardFunction(self, message): def CopyMessageToClipboardFunction(event): self.ParentWindow.SetCopyBuffer(message.GetFullText()) return CopyMessageToClipboardFunction - + def GetMessageByScreenPos(self, posx, posy): if self.CurrentMessage is not None: width, height = self.MessagePanel.GetClientSize() @@ -657,22 +658,22 @@ message = self.LogMessages[message_idx] draw_date = True offset = 5 - + while offset < height and message is not None: if draw_date: offset += DATE_INFO_SIZE - + if offset <= posy < offset + MESSAGE_INFO_SIZE: return message - + offset += MESSAGE_INFO_SIZE - + previous_message, message_idx = self.GetPreviousMessage(message_idx) if previous_message is not None: draw_date = message.Date != previous_message.Date message = previous_message return None - + def OnMessagePanelLeftUp(self, event): if self.CurrentMessage is not None: posx, posy = event.GetPosition() @@ -681,32 +682,32 @@ button.ProcessCallback() break event.Skip() - + def OnMessagePanelRightUp(self, event): message = self.GetMessageByScreenPos(*event.GetPosition()) if message is not None: menu = wx.Menu(title='') - + new_id = wx.NewId() menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Copy")) self.Bind(wx.EVT_MENU, self.GetCopyMessageToClipboardFunction(message), id=new_id) - + self.MessagePanel.PopupMenu(menu) menu.Destroy() event.Skip() - + def OnMessagePanelLeftDCLick(self, event): message = self.GetMessageByScreenPos(*event.GetPosition()) if message is not None: self.SearchMessage.SetFocus() self.SearchMessage.SetValue(message.Message) event.Skip() - + def ResetMessageToolTip(self): if self.MessageToolTip is not None: self.MessageToolTip.Destroy() self.MessageToolTip = None - + def OnMessageToolTipTimer(self, event): if self.LastMousePos is not None: message = self.GetMessageByScreenPos(*self.LastMousePos) @@ -719,31 +720,31 @@ self.MessageToolTip.SetToolTipPosition(tooltip_pos) self.MessageToolTip.Show() event.Skip() - + def OnMessagePanelMotion(self, event): if not event.Dragging(): self.ResetMessageToolTip() self.LastMousePos = event.GetPosition() self.MessageToolTipTimer.Start(int(TOOLTIP_WAIT_PERIOD * 1000), oneShot=True) event.Skip() - + def OnMessagePanelLeaveWindow(self, event): self.ResetMessageToolTip() self.LastMousePos = None self.MessageToolTipTimer.Stop() event.Skip() - + def OnMessagePanelMouseWheel(self, event): self.ScrollMessagePanel(event.GetWheelRotation() / event.GetWheelDelta()) event.Skip() - + def OnMessagePanelEraseBackground(self, event): pass - + def OnMessagePanelPaint(self, event): self.RefreshView() event.Skip() - + def OnMessagePanelResize(self, event): width, height = self.MessagePanel.GetClientSize() offset = 2 @@ -761,7 +762,7 @@ else: self.RefreshView() event.Skip() - + def OnScrollTimer(self, event): if self.ScrollSpeed != 0.: speed_norm = abs(self.ScrollSpeed) @@ -770,7 +771,7 @@ self.LastStartTime = gettime() self.ScrollTimer.Start(int(period * 1000), True) event.Skip() - + def SetScrollSpeed(self, speed): if speed == 0.: self.ScrollTimer.Stop() @@ -788,8 +789,8 @@ else: self.LastStartTime = current_time self.ScrollTimer.Start(int(period * 1000), True) - self.ScrollSpeed = speed - + self.ScrollSpeed = speed + def ScrollToLast(self, refresh=True): if len(self.LogMessages) > 0: self.CurrentMessage = len(self.LogMessages) - 1 diff -r e8daabf2c438 -r 826730e60407 runtime/WampClient.py --- a/runtime/WampClient.py Sun Feb 08 16:50:54 2015 +0100 +++ b/runtime/WampClient.py Sun Feb 08 22:39:17 2015 +0100 @@ -8,6 +8,7 @@ from twisted.internet.defer import inlineCallbacks from autobahn.wamp import types from autobahn.wamp.serializer import MsgPackSerializer +from twisted.internet.protocol import ReconnectingClientFactory import json _WampSession = None @@ -23,6 +24,7 @@ "GetTraceVariables", "RemoteExec", "GetLogMessage", + "ResetLogCount", ] def MakeCallee(name): @@ -47,6 +49,14 @@ _WampSession = None print 'WAMP session left' +class ReconnectingWampWebSocketClientFactory(WampWebSocketClientFactory, ReconnectingClientFactory): + def clientConnectionFailed(self, connector, reason): + print("WAMP Client connection failed .. retrying ..") + self.retry(connector) + def clientConnectionLost(self, connector, reason): + print("WAMP Client connection lost .. retrying ..") + self.retry(connector) + def RegisterWampClient(wampconf): WSClientConf = json.load(open(wampconf)) @@ -63,7 +73,7 @@ session_factory.session = WampSession # create a WAMP-over-WebSocket transport client factory - transport_factory = WampWebSocketClientFactory( + transport_factory = ReconnectingWampWebSocketClientFactory( session_factory, url = WSClientConf["url"], serializers = [MsgPackSerializer()],