Beremiz_service.py
changeset 330 fdf81615ed04
parent 319 bdd82ac65f5d
child 343 fe2d1936b546
equal deleted inserted replaced
329:22e65b8e20f4 330:fdf81615ed04
    25 import os, sys, getopt
    25 import os, sys, getopt
    26 
    26 
    27 def usage():
    27 def usage():
    28     print """
    28     print """
    29 Usage of Beremiz PLC execution service :\n
    29 Usage of Beremiz PLC execution service :\n
    30 %s {[-n name] [-i ip] [-p port] [-x enabletaskbar]|-h|--help} working_dir
    30 %s {[-n name] [-i ip] [-p port] [-x enabletaskbar] [-a autostart]|-h|--help} working_dir
    31            -n        - zeroconf service name
    31            -n        - zeroconf service name
    32            -i        - ip of interface to bind to (x.x.x.x)
    32            -i        - ip of interface to bind to (x.x.x.x)
    33            -p        - port number
    33            -p        - port number
    34            -h        - print this help text and quit
    34            -h        - print this help text and quit
       
    35            -a        - autostart PLC (0:disable 1:enable)
    35            -x        - enable/disable wxTaskbarIcon (0:disable 1:enable)
    36            -x        - enable/disable wxTaskbarIcon (0:disable 1:enable)
    36            
    37            
    37            working_dir - directory where are stored PLC files
    38            working_dir - directory where are stored PLC files
    38 """%sys.argv[0]
    39 """%sys.argv[0]
    39 
    40 
    40 try:
    41 try:
    41     opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:h")
    42     opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:a:h")
    42 except getopt.GetoptError, err:
    43 except getopt.GetoptError, err:
    43     # print help information and exit:
    44     # print help information and exit:
    44     print str(err) # will print something like "option -a not recognized"
    45     print str(err) # will print something like "option -a not recognized"
    45     usage()
    46     usage()
    46     sys.exit(2)
    47     sys.exit(2)
    50 port = 3000
    51 port = 3000
    51 name = os.environ[{
    52 name = os.environ[{
    52      "linux2":"USER",
    53      "linux2":"USER",
    53      "win32":"USERNAME",
    54      "win32":"USERNAME",
    54      }.get(sys.platform, "USER")]
    55      }.get(sys.platform, "USER")]
       
    56 autostart = False
    55 enablewx = True
    57 enablewx = True
    56 havewx = False
    58 havewx = False
    57 
    59 
    58 for o, a in opts:
    60 for o, a in opts:
    59     if o == "-h":
    61     if o == "-h":
    67         port = int(a)
    69         port = int(a)
    68     elif o == "-n":
    70     elif o == "-n":
    69         name = a
    71         name = a
    70     elif o == "-x":
    72     elif o == "-x":
    71         enablewx = int(a)
    73         enablewx = int(a)
       
    74     elif o == "-a":
       
    75         autostart = int(a)
    72     else:
    76     else:
    73         usage()
    77         usage()
    74         sys.exit()
    78         sys.exit()
    75 
    79 
    76 if len(argv) > 1:
    80 if len(argv) > 1:
    83     argv=[WorkingDir]
    87     argv=[WorkingDir]
    84 
    88 
    85 if enablewx:
    89 if enablewx:
    86     try:
    90     try:
    87         import wx, re
    91         import wx, re
    88         from threading import Thread
    92         from threading import Thread, currentThread
    89         from types import *
    93         from types import *
    90         havewx = True
    94         havewx = True
    91     except:
    95     except:
    92         havewx = False
    96         havewx = False
    93 
    97 
   234         
   238         
   235         def SetTests(self, tests):
   239         def SetTests(self, tests):
   236             self.Tests = tests
   240             self.Tests = tests
   237             
   241             
   238     class BeremizTaskBarIcon(wx.TaskBarIcon):
   242     class BeremizTaskBarIcon(wx.TaskBarIcon):
       
   243         TBMENU_START = wx.NewId()
       
   244         TBMENU_STOP = wx.NewId()
   239         TBMENU_CHANGE_NAME = wx.NewId()
   245         TBMENU_CHANGE_NAME = wx.NewId()
   240         TBMENU_CHANGE_PORT = wx.NewId()
   246         TBMENU_CHANGE_PORT = wx.NewId()
   241         TBMENU_CHANGE_INTERFACE = wx.NewId()
   247         TBMENU_CHANGE_INTERFACE = wx.NewId()
   242         TBMENU_LIVE_SHELL = wx.NewId()
   248         TBMENU_LIVE_SHELL = wx.NewId()
   243         TBMENU_WXINSPECTOR = wx.NewId()
   249         TBMENU_WXINSPECTOR = wx.NewId()
   249             self.pyroserver = pyroserver
   255             self.pyroserver = pyroserver
   250             # Set the image
   256             # Set the image
   251             self.UpdateIcon(None)
   257             self.UpdateIcon(None)
   252     
   258     
   253             # bind some events
   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)
   254             self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME)
   262             self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME)
   255             self.Bind(wx.EVT_MENU, self.OnTaskBarChangeInterface, id=self.TBMENU_CHANGE_INTERFACE)
   263             self.Bind(wx.EVT_MENU, self.OnTaskBarChangeInterface, id=self.TBMENU_CHANGE_INTERFACE)
   256             self.Bind(wx.EVT_MENU, self.OnTaskBarLiveShell, id=self.TBMENU_LIVE_SHELL)
   264             self.Bind(wx.EVT_MENU, self.OnTaskBarLiveShell, id=self.TBMENU_LIVE_SHELL)
   257             self.Bind(wx.EVT_MENU, self.OnTaskBarWXInspector, id=self.TBMENU_WXINSPECTOR)
   265             self.Bind(wx.EVT_MENU, self.OnTaskBarWXInspector, id=self.TBMENU_WXINSPECTOR)
   258             self.Bind(wx.EVT_MENU, self.OnTaskBarChangePort, id=self.TBMENU_CHANGE_PORT)
   266             self.Bind(wx.EVT_MENU, self.OnTaskBarChangePort, id=self.TBMENU_CHANGE_PORT)
   265             the menu for the default EVT_RIGHT_DOWN event.  Just create
   273             the menu for the default EVT_RIGHT_DOWN event.  Just create
   266             the menu how you want it and return it from this function,
   274             the menu how you want it and return it from this function,
   267             the base class takes care of the rest.
   275             the base class takes care of the rest.
   268             """
   276             """
   269             menu = wx.Menu()
   277             menu = wx.Menu()
       
   278             menu.Append(self.TBMENU_START, "Start PLC")
       
   279             menu.Append(self.TBMENU_STOP, "Stop PLC")
   270             menu.Append(self.TBMENU_CHANGE_NAME, "Change Name")
   280             menu.Append(self.TBMENU_CHANGE_NAME, "Change Name")
   271             menu.Append(self.TBMENU_CHANGE_INTERFACE, "Change IP of interface to bind")
   281             menu.Append(self.TBMENU_CHANGE_INTERFACE, "Change IP of interface to bind")
   272             menu.Append(self.TBMENU_LIVE_SHELL, "Launch a live Python shell")
   282             menu.Append(self.TBMENU_LIVE_SHELL, "Launch a live Python shell")
   273             menu.Append(self.TBMENU_WXINSPECTOR, "Launch WX GUI inspector")
   283             menu.Append(self.TBMENU_WXINSPECTOR, "Launch WX GUI inspector")
   274             menu.Append(self.TBMENU_CHANGE_PORT, "Change Port Number")
   284             menu.Append(self.TBMENU_CHANGE_PORT, "Change Port Number")
   288                 img = img.Scale(22, 22)
   298                 img = img.Scale(22, 22)
   289             # wxMac can be any size upto 128x128, so leave the source img alone....
   299             # wxMac can be any size upto 128x128, so leave the source img alone....
   290             icon = wx.IconFromBitmap(img.ConvertToBitmap() )
   300             icon = wx.IconFromBitmap(img.ConvertToBitmap() )
   291             return icon
   301             return icon
   292         
   302         
   293         def OnTaskBarChangeInterface(self,evt):
   303         def OnTaskBarStartPLC(self, evt):
   294             dlg = ParamsEntryDialog(None, "Enter the ip of the interface to bind", defaultValue=pyroserver.ip)
   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)
   295             dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, "Ip is not valid!"),
   315             dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, "Ip is not valid!"),
   296                            ( lambda ip :len([x for x in ip.split(".") if 0 <= int(x) <= 255]) == 4, "Ip is not valid!")
   316                            ( lambda ip :len([x for x in ip.split(".") if 0 <= int(x) <= 255]) == 4, "Ip is not valid!")
   297                            ])
   317                            ])
   298             if dlg.ShowModal() == wx.ID_OK:
   318             if dlg.ShowModal() == wx.ID_OK:
   299                 self.pyroserver.ip = dlg.GetValue()
   319                 self.pyroserver.ip = dlg.GetValue()
   300                 self.pyroserver.Stop()
   320                 self.pyroserver.Stop()
   301             evt.Skip()
   321             evt.Skip()
   302                 
   322                 
   303         def OnTaskBarChangePort(self,evt):
   323         def OnTaskBarChangePort(self, evt):
   304             dlg = ParamsEntryDialog(None, "Enter a port number ", defaultValue=str(pyroserver.port))
   324             dlg = ParamsEntryDialog(None, "Enter a port number ", defaultValue=str(self.pyroserver.port))
   305             dlg.SetTests([(UnicodeType.isdigit, "Port number must be an integer!"), (lambda port : 0 <= int(port) <= 65535 , "Port number must be 0 <= port <= 65535!")])
   325             dlg.SetTests([(UnicodeType.isdigit, "Port number must be an integer!"), (lambda port : 0 <= int(port) <= 65535 , "Port number must be 0 <= port <= 65535!")])
   306             if dlg.ShowModal() == wx.ID_OK:
   326             if dlg.ShowModal() == wx.ID_OK:
   307                 self.pyroserver.port = int(dlg.GetValue())
   327                 self.pyroserver.port = int(dlg.GetValue())
   308                 self.pyroserver.Stop()
   328                 self.pyroserver.Stop()
   309             evt.Skip()
   329             evt.Skip()
   310         
   330         
   311         def OnTaskBarChangeWorkingDir(self,evt):
   331         def OnTaskBarChangeWorkingDir(self, evt):
   312             dlg = wx.DirDialog(None, "Choose a working directory ", pyroserver.workdir, wx.DD_NEW_DIR_BUTTON)
   332             dlg = wx.DirDialog(None, "Choose a working directory ", self.pyroserver.workdir, wx.DD_NEW_DIR_BUTTON)
   313             if dlg.ShowModal() == wx.ID_OK:
   333             if dlg.ShowModal() == wx.ID_OK:
   314                 self.pyroserver.workdir = dlg.GetPath()
   334                 self.pyroserver.workdir = dlg.GetPath()
   315                 self.pyroserver.Stop()
   335                 self.pyroserver.Stop()
   316             evt.Skip()
   336             evt.Skip()
   317                 
   337                 
   318         def OnTaskBarChangeName(self,evt):
   338         def OnTaskBarChangeName(self, evt):
   319             dlg = ParamsEntryDialog(None, "Enter a name ", defaultValue=pyroserver.name)
   339             dlg = ParamsEntryDialog(None, "Enter a name ", defaultValue=self.pyroserver.name)
   320             dlg.SetTests([(lambda name : len(name) is not 0 , "Name must not be null!")])
   340             dlg.SetTests([(lambda name : len(name) is not 0 , "Name must not be null!")])
   321             if dlg.ShowModal() == wx.ID_OK:
   341             if dlg.ShowModal() == wx.ID_OK:
   322                 self.pyroserver.name = dlg.GetValue()
   342                 self.pyroserver.name = dlg.GetValue()
   323                 self.pyroserver.Restart()
   343                 self.pyroserver.Restart()
   324             evt.Skip()
   344             evt.Skip()
   325 
   345 
   326         def OnTaskBarLiveShell(self,evt):
   346         def OnTaskBarLiveShell(self, evt):
   327             if self.pyroserver.plcobj is not None and self.pyroserver.plcobj.python_threads_vars is not None:
   347             if self.pyroserver.plcobj is not None and self.pyroserver.plcobj.python_threads_vars is not None:
   328                 from wx import py
   348                 from wx import py
   329                 #frame = py.shell.ShellFrame(locals=self.pyroserver.plcobj.python_threads_vars)
   349                 #frame = py.shell.ShellFrame(locals=self.pyroserver.plcobj.python_threads_vars)
   330                 frame = py.crust.CrustFrame(locals=self.pyroserver.plcobj.python_threads_vars)
   350                 frame = py.crust.CrustFrame(locals=self.pyroserver.plcobj.python_threads_vars)
   331                 frame.Show()
   351                 frame.Show()
   345             if not wnd:
   365             if not wnd:
   346                 wnd = wx.GetApp()
   366                 wnd = wx.GetApp()
   347             InspectionTool().Show(wnd, True)
   367             InspectionTool().Show(wnd, True)
   348             evt.Skip()
   368             evt.Skip()
   349 
   369 
   350         def OnTaskBarQuit(self,evt):
   370         def OnTaskBarQuit(self, evt):
   351             pyroserver.Quit()
   371             self.pyroserver.Quit()
   352             self.RemoveIcon()
   372             self.RemoveIcon()
   353             wx.GetApp().ExitMainLoop()
   373             wx.CallAfter(wx.GetApp().Exit)
   354             evt.Skip()
   374             evt.Skip()
   355             
   375             
   356         def UpdateIcon(self, plcstatus):
   376         def UpdateIcon(self, plcstatus):
   357             if plcstatus is "Started" :
   377             if plcstatus is "Started" :
   358                 currenticon = self.MakeIcon(starticon.GetImage())
   378                 currenticon = self.MakeIcon(starticon.GetImage())
   370 
   390 
   371 def default_evaluator(callable, *args, **kwargs):
   391 def default_evaluator(callable, *args, **kwargs):
   372     return callable(*args,**kwargs)
   392     return callable(*args,**kwargs)
   373 
   393 
   374 class Server():
   394 class Server():
   375     def __init__(self, name, ip, port, workdir, argv, statuschange=None, evaluator=default_evaluator):
   395     def __init__(self, name, ip, port, workdir, argv, autostart=False, statuschange=None, evaluator=default_evaluator):
   376         self.continueloop = True
   396         self.continueloop = True
   377         self.daemon = None
   397         self.daemon = None
   378         self.name = name
   398         self.name = name
   379         self.ip = ip
   399         self.ip = ip
   380         self.port = port
   400         self.port = port
   381         self.workdir = workdir
   401         self.workdir = workdir
   382         self.argv = argv
   402         self.argv = argv
   383         self.plcobj = None
   403         self.plcobj = None
   384         self.servicepublisher = None
   404         self.servicepublisher = None
       
   405         self.autostart = autostart
   385         self.statuschange = statuschange
   406         self.statuschange = statuschange
   386         self.evaluator = evaluator
   407         self.evaluator = evaluator
   387     
   408     
   388     def Loop(self):
   409     def Loop(self):
   389         while self.continueloop:
   410         while self.continueloop:
   408         
   429         
   409         # Configure and publish service
   430         # Configure and publish service
   410         # Not publish service if localhost in address params
   431         # Not publish service if localhost in address params
   411         if self.ip != "localhost" and self.ip != "127.0.0.1":    
   432         if self.ip != "localhost" and self.ip != "127.0.0.1":    
   412             print "Publish service on local network"
   433             print "Publish service on local network"
   413             self.servicepublisher = ServicePublisher.ServicePublisher()            
   434             self.servicepublisher = ServicePublisher.ServicePublisher()
   414             self.servicepublisher.RegisterService(self.name, self.ip, self.port)
   435             self.servicepublisher.RegisterService(self.name, self.ip, self.port)
   415         
   436         
       
   437         if self.autostart:
       
   438             self.plcobj.StartPLC()
       
   439         
   416         sys.stdout.flush()
   440         sys.stdout.flush()
   417         
   441         
   418         self.daemon.requestLoop()
   442         self.daemon.requestLoop()
   419     
   443     
   420     def Stop(self):
   444     def Stop(self):
       
   445         self.plcobj.StopPLC()
   421         if self.servicepublisher is not None:
   446         if self.servicepublisher is not None:
   422             self.servicepublisher.UnRegisterService()
   447             self.servicepublisher.UnRegisterService()
   423             del self.servicepublisher
   448             del self.servicepublisher
   424         self.daemon.shutdown(True)
   449         self.daemon.shutdown(True)
   425         
   450         
   426 
   451 
   427 if havewx:
   452 if havewx:
   428     from threading import Semaphore
   453     from threading import Semaphore
   429     wx_eval_lock = Semaphore(0)
   454     wx_eval_lock = Semaphore(0)
   430     app=wx.App(redirect=False)
   455     app=wx.App(redirect=False)
       
   456     mythread = currentThread()
       
   457     
   431     def statuschange(status):
   458     def statuschange(status):
   432         wx.CallAfter(taskbar_instance.UpdateIcon,status)
   459         wx.CallAfter(taskbar_instance.UpdateIcon,status)
   433         
   460         
   434     eval_res = None
   461     eval_res = None
   435     def wx_evaluator(callable, *args, **kwargs):
   462     def wx_evaluator(callable, *args, **kwargs):
   437         eval_res=callable(*args,**kwargs)
   464         eval_res=callable(*args,**kwargs)
   438         #print eval_res
   465         #print eval_res
   439         wx_eval_lock.release()
   466         wx_eval_lock.release()
   440         
   467         
   441     def evaluator(callable, *args, **kwargs):
   468     def evaluator(callable, *args, **kwargs):
   442         wx.CallAfter(wx_evaluator,callable,*args,**kwargs)
   469         # call directly the callable function if call from the wx mainloop (avoid dead lock) 
   443         wx_eval_lock.acquire()
   470         if(mythread == currentThread()):
       
   471             callable(*args,**kwargs)
       
   472         else:
       
   473             wx.CallAfter(wx_evaluator,callable,*args,**kwargs)
       
   474             wx_eval_lock.acquire()
   444         return eval_res
   475         return eval_res
   445 
   476 
   446     pyroserver = Server(name, ip, port, WorkingDir, argv, statuschange, evaluator)
   477     pyroserver = Server(name, ip, port, WorkingDir, argv, autostart, statuschange, evaluator)
   447     taskbar_instance = BeremizTaskBarIcon(pyroserver)
   478     taskbar_instance = BeremizTaskBarIcon(pyroserver)
   448     
   479     
   449     pyro_thread=Thread(target=pyroserver.Loop)
   480     pyro_thread=Thread(target=pyroserver.Loop)
   450     pyro_thread.start()
   481     pyro_thread.start()
   451     app.MainLoop()
   482     app.MainLoop()
   452 else:
   483 else:
   453     pyroserver = Server(name, ip, port, WorkingDir, argv)
   484     pyroserver = Server(name, ip, port, WorkingDir, argv, autostart)
   454     pyroserver.Loop()
   485     pyroserver.Loop()