Beremiz_service.py
changeset 343 fe2d1936b546
parent 330 fdf81615ed04
child 361 331d698e1118
equal deleted inserted replaced
342:80e5876bc53b 343:fe2d1936b546
   198         "8LszMRweXLr7kWB35oMdCAT+1jRt0cqVK6Otra2+hvoGGuobWPLEEsoXzkbPkLhvR4CBRwJY"
   198         "8LszMRweXLr7kWB35oMdCAT+1jRt0cqVK6Otra2+hvoGGuobWPLEEsoXzkbPkLhvR4CBRwJY"
   199         "Xq/3SGVlZbq7u7utsrJyxDTNz06cOJHZ0tRCS1MLAKuRwNQT9v8AyV27dn1fXl7eqmlae11d"
   199         "Xq/3SGVlZbq7u7utsrJyxDTNz06cOJHZ0tRCS1MLAKuRwNQT9v8AyV27dn1fXl7eqmlae11d"
   200         "XXLfvn0/+Xy+l6LR6Gu2befFYjFfzrk2FzeHp7mK7jdxz2/LffGamhpvc3NzyLKsbFd3z1PG"
   200         "XXLfvn0/+Xy+l6LR6Gu2befFYjFfzrk2FzeHp7mK7jdxz2/LffGamhpvc3NzyLKsbFd3z1PG"
   201         "aHyBTKdjum0POGzbFAp7qo0xVOtJZdf/C/wRDnL5FYGSAAAAAElFTkSuQmCC")
   201         "aHyBTKdjum0POGzbFAp7qo0xVOtJZdf/C/wRDnL5FYGSAAAAAElFTkSuQmCC")
   202         
   202         
   203     class ParamsEntryDialog(wx.TextEntryDialog):
   203         class ParamsEntryDialog(wx.TextEntryDialog):
   204         if wx.VERSION < (2, 6, 0):
   204             if wx.VERSION < (2, 6, 0):
   205             def Bind(self, event, function, id = None):
   205                 def Bind(self, event, function, id = None):
   206                 if id is not None:
   206                     if id is not None:
   207                     event(self, id, function)
   207                         event(self, id, function)
       
   208                     else:
       
   209                         event(self, function)
       
   210             
       
   211             
       
   212             def __init__(self, parent, message, caption = "Please enter text", defaultValue = "", 
       
   213                                style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
       
   214                 wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
       
   215                 
       
   216                 self.Tests = []
       
   217                 if wx.VERSION >= (2, 8, 0):
       
   218                     self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId())
       
   219                 elif wx.VERSION >= (2, 6, 0):
       
   220                     self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId())
   208                 else:
   221                 else:
   209                     event(self, function)
   222                     self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
   210         
   223             
   211     
   224             def OnOK(self, event):
   212         def __init__(self, parent, message, caption = "Please enter text", defaultValue = "", 
   225                 value = self.GetValue()
   213                            style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
   226                 texts = {"value" : value}
   214             wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
   227                 for function, message in self.Tests:
   215             
   228                     if not function(value):
   216             self.Tests = []
   229                         message = wx.MessageDialog(self, message%texts, "Error", wx.OK|wx.ICON_ERROR)
   217             if wx.VERSION >= (2, 8, 0):
   230                         message.ShowModal()
   218                 self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId())
   231                         message.Destroy()
   219             elif wx.VERSION >= (2, 6, 0):
   232                         return
   220                 self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId())
   233                 self.EndModal(wx.ID_OK)
   221             else:
   234                 event.Skip()
   222                 self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
   235             
   223         
   236             def GetValue(self):
   224         def OnOK(self, event):
   237                 return self.GetSizer().GetItem(1).GetWindow().GetValue()
   225             value = self.GetValue()
   238             
   226             texts = {"value" : value}
   239             def SetTests(self, tests):
   227             for function, message in self.Tests:
   240                 self.Tests = tests
   228                 if not function(value):
   241         
   229                     message = wx.MessageDialog(self, message%texts, "Error", wx.OK|wx.ICON_ERROR)
   242         class BeremizTaskBarIcon(wx.TaskBarIcon):
   230                     message.ShowModal()
   243             TBMENU_START = wx.NewId()
   231                     message.Destroy()
   244             TBMENU_STOP = wx.NewId()
   232                     return
   245             TBMENU_CHANGE_NAME = wx.NewId()
   233             self.EndModal(wx.ID_OK)
   246             TBMENU_CHANGE_PORT = wx.NewId()
   234             event.Skip()
   247             TBMENU_CHANGE_INTERFACE = wx.NewId()
   235         
   248             TBMENU_LIVE_SHELL = wx.NewId()
   236         def GetValue(self):
   249             TBMENU_WXINSPECTOR = wx.NewId()
   237             return self.GetSizer().GetItem(1).GetWindow().GetValue()
   250             TBMENU_CHANGE_WD = wx.NewId()
   238         
   251             TBMENU_QUIT = wx.NewId()
   239         def SetTests(self, tests):
   252             
   240             self.Tests = tests
   253             def __init__(self, pyroserver):
   241             
   254                 wx.TaskBarIcon.__init__(self)
   242     class BeremizTaskBarIcon(wx.TaskBarIcon):
   255                 self.pyroserver = pyroserver
   243         TBMENU_START = wx.NewId()
   256                 # Set the image
   244         TBMENU_STOP = wx.NewId()
   257                 self.UpdateIcon(None)
   245         TBMENU_CHANGE_NAME = wx.NewId()
       
   246         TBMENU_CHANGE_PORT = wx.NewId()
       
   247         TBMENU_CHANGE_INTERFACE = wx.NewId()
       
   248         TBMENU_LIVE_SHELL = wx.NewId()
       
   249         TBMENU_WXINSPECTOR = wx.NewId()
       
   250         TBMENU_CHANGE_WD = wx.NewId()
       
   251         TBMENU_QUIT = wx.NewId()
       
   252         
       
   253         def __init__(self, pyroserver):
       
   254             wx.TaskBarIcon.__init__(self)
       
   255             self.pyroserver = pyroserver
       
   256             # Set the image
       
   257             self.UpdateIcon(None)
       
   258     
       
   259             # bind some events
       
   260             self.Bind(wx.EVT_MENU, self.OnTaskBarStartPLC, id=self.TBMENU_START)
       
   261             self.Bind(wx.EVT_MENU, self.OnTaskBarStopPLC, id=self.TBMENU_STOP)
       
   262             self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME)
       
   263             self.Bind(wx.EVT_MENU, self.OnTaskBarChangeInterface, id=self.TBMENU_CHANGE_INTERFACE)
       
   264             self.Bind(wx.EVT_MENU, self.OnTaskBarLiveShell, id=self.TBMENU_LIVE_SHELL)
       
   265             self.Bind(wx.EVT_MENU, self.OnTaskBarWXInspector, id=self.TBMENU_WXINSPECTOR)
       
   266             self.Bind(wx.EVT_MENU, self.OnTaskBarChangePort, id=self.TBMENU_CHANGE_PORT)
       
   267             self.Bind(wx.EVT_MENU, self.OnTaskBarChangeWorkingDir, id=self.TBMENU_CHANGE_WD)
       
   268             self.Bind(wx.EVT_MENU, self.OnTaskBarQuit, id=self.TBMENU_QUIT)
       
   269         
       
   270         def CreatePopupMenu(self):
       
   271             """
       
   272             This method is called by the base class when it needs to popup
       
   273             the menu for the default EVT_RIGHT_DOWN event.  Just create
       
   274             the menu how you want it and return it from this function,
       
   275             the base class takes care of the rest.
       
   276             """
       
   277             menu = wx.Menu()
       
   278             menu.Append(self.TBMENU_START, "Start PLC")
       
   279             menu.Append(self.TBMENU_STOP, "Stop PLC")
       
   280             menu.Append(self.TBMENU_CHANGE_NAME, "Change Name")
       
   281             menu.Append(self.TBMENU_CHANGE_INTERFACE, "Change IP of interface to bind")
       
   282             menu.Append(self.TBMENU_LIVE_SHELL, "Launch a live Python shell")
       
   283             menu.Append(self.TBMENU_WXINSPECTOR, "Launch WX GUI inspector")
       
   284             menu.Append(self.TBMENU_CHANGE_PORT, "Change Port Number")
       
   285             menu.AppendSeparator()
       
   286             menu.Append(self.TBMENU_CHANGE_WD, "Change working directory")
       
   287             menu.Append(self.TBMENU_QUIT, "Quit")
       
   288             return menu
       
   289     
       
   290         def MakeIcon(self, img):
       
   291             """
       
   292             The various platforms have different requirements for the
       
   293             icon size...
       
   294             """
       
   295             if "wxMSW" in wx.PlatformInfo:
       
   296                 img = img.Scale(16, 16)
       
   297             elif "wxGTK" in wx.PlatformInfo:
       
   298                 img = img.Scale(22, 22)
       
   299             # wxMac can be any size upto 128x128, so leave the source img alone....
       
   300             icon = wx.IconFromBitmap(img.ConvertToBitmap() )
       
   301             return icon
       
   302         
       
   303         def OnTaskBarStartPLC(self, evt):
       
   304             if self.pyroserver.plcobj is not None: 
       
   305                 self.pyroserver.plcobj.StartPLC()
       
   306             evt.Skip()
       
   307             
       
   308         def OnTaskBarStopPLC(self, evt):
       
   309             if self.pyroserver.plcobj is not None:
       
   310                 self.pyroserver.plcobj.StopPLC()
       
   311             evt.Skip()
       
   312             
       
   313         def OnTaskBarChangeInterface(self, evt):
       
   314             dlg = ParamsEntryDialog(None, "Enter the ip of the interface to bind", defaultValue=self.pyroserver.ip)
       
   315             dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, "Ip is not valid!"),
       
   316                            ( lambda ip :len([x for x in ip.split(".") if 0 <= int(x) <= 255]) == 4, "Ip is not valid!")
       
   317                            ])
       
   318             if dlg.ShowModal() == wx.ID_OK:
       
   319                 self.pyroserver.ip = dlg.GetValue()
       
   320                 self.pyroserver.Stop()
       
   321             evt.Skip()
       
   322                 
   258                 
   323         def OnTaskBarChangePort(self, evt):
   259                 # bind some events
   324             dlg = ParamsEntryDialog(None, "Enter a port number ", defaultValue=str(self.pyroserver.port))
   260                 self.Bind(wx.EVT_MENU, self.OnTaskBarStartPLC, id=self.TBMENU_START)
   325             dlg.SetTests([(UnicodeType.isdigit, "Port number must be an integer!"), (lambda port : 0 <= int(port) <= 65535 , "Port number must be 0 <= port <= 65535!")])
   261                 self.Bind(wx.EVT_MENU, self.OnTaskBarStopPLC, id=self.TBMENU_STOP)
   326             if dlg.ShowModal() == wx.ID_OK:
   262                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME)
   327                 self.pyroserver.port = int(dlg.GetValue())
   263                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangeInterface, id=self.TBMENU_CHANGE_INTERFACE)
   328                 self.pyroserver.Stop()
   264                 self.Bind(wx.EVT_MENU, self.OnTaskBarLiveShell, id=self.TBMENU_LIVE_SHELL)
   329             evt.Skip()
   265                 self.Bind(wx.EVT_MENU, self.OnTaskBarWXInspector, id=self.TBMENU_WXINSPECTOR)
   330         
   266                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangePort, id=self.TBMENU_CHANGE_PORT)
   331         def OnTaskBarChangeWorkingDir(self, evt):
   267                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangeWorkingDir, id=self.TBMENU_CHANGE_WD)
   332             dlg = wx.DirDialog(None, "Choose a working directory ", self.pyroserver.workdir, wx.DD_NEW_DIR_BUTTON)
   268                 self.Bind(wx.EVT_MENU, self.OnTaskBarQuit, id=self.TBMENU_QUIT)
   333             if dlg.ShowModal() == wx.ID_OK:
   269             
   334                 self.pyroserver.workdir = dlg.GetPath()
   270             def CreatePopupMenu(self):
   335                 self.pyroserver.Stop()
   271                 """
   336             evt.Skip()
   272                 This method is called by the base class when it needs to popup
       
   273                 the menu for the default EVT_RIGHT_DOWN event.  Just create
       
   274                 the menu how you want it and return it from this function,
       
   275                 the base class takes care of the rest.
       
   276                 """
       
   277                 menu = wx.Menu()
       
   278                 menu.Append(self.TBMENU_START, "Start PLC")
       
   279                 menu.Append(self.TBMENU_STOP, "Stop PLC")
       
   280                 menu.Append(self.TBMENU_CHANGE_NAME, "Change Name")
       
   281                 menu.Append(self.TBMENU_CHANGE_INTERFACE, "Change IP of interface to bind")
       
   282                 menu.Append(self.TBMENU_LIVE_SHELL, "Launch a live Python shell")
       
   283                 menu.Append(self.TBMENU_WXINSPECTOR, "Launch WX GUI inspector")
       
   284                 menu.Append(self.TBMENU_CHANGE_PORT, "Change Port Number")
       
   285                 menu.AppendSeparator()
       
   286                 menu.Append(self.TBMENU_CHANGE_WD, "Change working directory")
       
   287                 menu.Append(self.TBMENU_QUIT, "Quit")
       
   288                 return menu
       
   289             
       
   290             def MakeIcon(self, img):
       
   291                 """
       
   292                 The various platforms have different requirements for the
       
   293                 icon size...
       
   294                 """
       
   295                 if "wxMSW" in wx.PlatformInfo:
       
   296                     img = img.Scale(16, 16)
       
   297                 elif "wxGTK" in wx.PlatformInfo:
       
   298                     img = img.Scale(22, 22)
       
   299                 # wxMac can be any size upto 128x128, so leave the source img alone....
       
   300                 icon = wx.IconFromBitmap(img.ConvertToBitmap() )
       
   301                 return icon
       
   302             
       
   303             def OnTaskBarStartPLC(self, evt):
       
   304                 if self.pyroserver.plcobj is not None: 
       
   305                     self.pyroserver.plcobj.StartPLC()
       
   306                 evt.Skip()
       
   307             
       
   308             def OnTaskBarStopPLC(self, evt):
       
   309                 if self.pyroserver.plcobj is not None:
       
   310                     self.pyroserver.plcobj.StopPLC()
       
   311                 evt.Skip()
       
   312             
       
   313             def OnTaskBarChangeInterface(self, evt):
       
   314                 dlg = ParamsEntryDialog(None, "Enter the ip of the interface to bind", defaultValue=self.pyroserver.ip)
       
   315                 dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, "Ip is not valid!"),
       
   316                                ( lambda ip :len([x for x in ip.split(".") if 0 <= int(x) <= 255]) == 4, "Ip is not valid!")
       
   317                                ])
       
   318                 if dlg.ShowModal() == wx.ID_OK:
       
   319                     self.pyroserver.ip = dlg.GetValue()
       
   320                     self.pyroserver.Stop()
       
   321                 evt.Skip()
       
   322             
       
   323             def OnTaskBarChangePort(self, evt):
       
   324                 dlg = ParamsEntryDialog(None, "Enter a port number ", defaultValue=str(self.pyroserver.port))
       
   325                 dlg.SetTests([(UnicodeType.isdigit, "Port number must be an integer!"), (lambda port : 0 <= int(port) <= 65535 , "Port number must be 0 <= port <= 65535!")])
       
   326                 if dlg.ShowModal() == wx.ID_OK:
       
   327                     self.pyroserver.port = int(dlg.GetValue())
       
   328                     self.pyroserver.Stop()
       
   329                 evt.Skip()
       
   330             
       
   331             def OnTaskBarChangeWorkingDir(self, evt):
       
   332                 dlg = wx.DirDialog(None, "Choose a working directory ", self.pyroserver.workdir, wx.DD_NEW_DIR_BUTTON)
       
   333                 if dlg.ShowModal() == wx.ID_OK:
       
   334                     self.pyroserver.workdir = dlg.GetPath()
       
   335                     self.pyroserver.Stop()
       
   336                 evt.Skip()
       
   337             
       
   338             def OnTaskBarChangeName(self, evt):
       
   339                 dlg = ParamsEntryDialog(None, "Enter a name ", defaultValue=self.pyroserver.name)
       
   340                 dlg.SetTests([(lambda name : len(name) is not 0 , "Name must not be null!")])
       
   341                 if dlg.ShowModal() == wx.ID_OK:
       
   342                     self.pyroserver.name = dlg.GetValue()
       
   343                     self.pyroserver.Restart()
       
   344                 evt.Skip()
       
   345             
       
   346             def OnTaskBarLiveShell(self, evt):
       
   347                 if self.pyroserver.plcobj is not None and self.pyroserver.plcobj.python_threads_vars is not None:
       
   348                     from wx import py
       
   349                     #frame = py.shell.ShellFrame(locals=self.pyroserver.plcobj.python_threads_vars)
       
   350                     frame = py.crust.CrustFrame(locals=self.pyroserver.plcobj.python_threads_vars)
       
   351                     frame.Show()
       
   352                 else:
       
   353                     wx.MessageBox("No runnning PLC","Error")
       
   354                 evt.Skip()
       
   355             
       
   356             def OnTaskBarWXInspector(self, evt):
       
   357                 # Activate the widget inspection tool
       
   358                 from wx.lib.inspection import InspectionTool
       
   359                 if not InspectionTool().initialized:
       
   360                     InspectionTool().Init(locals=self.pyroserver.plcobj.python_threads_vars)
   337                 
   361                 
   338         def OnTaskBarChangeName(self, evt):
   362                 # Find a widget to be selected in the tree.  Use either the
   339             dlg = ParamsEntryDialog(None, "Enter a name ", defaultValue=self.pyroserver.name)
   363                 # one under the cursor, if any, or this frame.
   340             dlg.SetTests([(lambda name : len(name) is not 0 , "Name must not be null!")])
   364                 wnd = wx.FindWindowAtPointer()
   341             if dlg.ShowModal() == wx.ID_OK:
   365                 if not wnd:
   342                 self.pyroserver.name = dlg.GetValue()
   366                     wnd = wx.GetApp()
   343                 self.pyroserver.Restart()
   367                 InspectionTool().Show(wnd, True)
   344             evt.Skip()
   368                 evt.Skip()
   345 
   369             
   346         def OnTaskBarLiveShell(self, evt):
   370             def OnTaskBarQuit(self, evt):
   347             if self.pyroserver.plcobj is not None and self.pyroserver.plcobj.python_threads_vars is not None:
   371                 self.pyroserver.Quit()
   348                 from wx import py
   372                 self.RemoveIcon()
   349                 #frame = py.shell.ShellFrame(locals=self.pyroserver.plcobj.python_threads_vars)
   373                 wx.CallAfter(wx.GetApp().Exit)
   350                 frame = py.crust.CrustFrame(locals=self.pyroserver.plcobj.python_threads_vars)
   374                 evt.Skip()
   351                 frame.Show()
   375             
   352             else:
   376             def UpdateIcon(self, plcstatus):
   353                 wx.MessageBox("No runnning PLC","Error")
   377                 if plcstatus is "Started" :
   354             evt.Skip()
   378                     currenticon = self.MakeIcon(starticon.GetImage())
   355 
   379                 elif plcstatus is "Stopped":
   356         def OnTaskBarWXInspector(self, evt):
   380                     currenticon = self.MakeIcon(stopicon.GetImage())
   357             # Activate the widget inspection tool
   381                 else:
   358             from wx.lib.inspection import InspectionTool
   382                     currenticon = self.MakeIcon(defaulticon.GetImage())
   359             if not InspectionTool().initialized:
   383                 self.SetIcon(currenticon, "Beremiz Service")
   360                 InspectionTool().Init(locals=self.pyroserver.plcobj.python_threads_vars)
   384 
   361     
   385 from runtime import PLCObject, PLCprint, ServicePublisher
   362             # Find a widget to be selected in the tree.  Use either the
       
   363             # one under the cursor, if any, or this frame.
       
   364             wnd = wx.FindWindowAtPointer()
       
   365             if not wnd:
       
   366                 wnd = wx.GetApp()
       
   367             InspectionTool().Show(wnd, True)
       
   368             evt.Skip()
       
   369 
       
   370         def OnTaskBarQuit(self, evt):
       
   371             self.pyroserver.Quit()
       
   372             self.RemoveIcon()
       
   373             wx.CallAfter(wx.GetApp().Exit)
       
   374             evt.Skip()
       
   375             
       
   376         def UpdateIcon(self, plcstatus):
       
   377             if plcstatus is "Started" :
       
   378                 currenticon = self.MakeIcon(starticon.GetImage())
       
   379             elif plcstatus is "Stopped":
       
   380                 currenticon = self.MakeIcon(stopicon.GetImage())
       
   381             else:
       
   382                 currenticon = self.MakeIcon(defaulticon.GetImage())
       
   383             self.SetIcon(currenticon, "Beremiz Service")
       
   384 
       
   385 from runtime import PLCObject, ServicePublisher
       
   386 import Pyro.core as pyro
   386 import Pyro.core as pyro
   387 
   387 
   388 if not os.path.isdir(WorkingDir):
   388 if not os.path.isdir(WorkingDir):
   389     os.mkdir(WorkingDir)
   389     os.mkdir(WorkingDir)
   390 
   390 
   459         wx.CallAfter(taskbar_instance.UpdateIcon,status)
   459         wx.CallAfter(taskbar_instance.UpdateIcon,status)
   460         
   460         
   461     eval_res = None
   461     eval_res = None
   462     def wx_evaluator(callable, *args, **kwargs):
   462     def wx_evaluator(callable, *args, **kwargs):
   463         global eval_res
   463         global eval_res
   464         eval_res=callable(*args,**kwargs)
   464         try:
   465         #print eval_res
   465             eval_res=callable(*args,**kwargs)
   466         wx_eval_lock.release()
   466         except Exception,e:
       
   467             PLCprint("#EXCEPTION : "+str(e))
       
   468         finally:
       
   469             wx_eval_lock.release()
   467         
   470         
   468     def evaluator(callable, *args, **kwargs):
   471     def evaluator(callable, *args, **kwargs):
   469         # call directly the callable function if call from the wx mainloop (avoid dead lock) 
   472         # call directly the callable function if call from the wx mainloop (avoid dead lock) 
   470         if(mythread == currentThread()):
   473         if(mythread == currentThread()):
   471             callable(*args,**kwargs)
   474             callable(*args,**kwargs)