Beremiz_service.py
changeset 2270 d9175daf6522
parent 2249 602fdd08dfab
child 2273 a0efe3d9c853
equal deleted inserted replaced
2269:2e38b5ec4753 2270:d9175daf6522
    29 import os
    29 import os
    30 import sys
    30 import sys
    31 import getopt
    31 import getopt
    32 import threading
    32 import threading
    33 from threading import Thread, Semaphore, Lock
    33 from threading import Thread, Semaphore, Lock
    34 import traceback
       
    35 import __builtin__
    34 import __builtin__
    36 import Pyro
    35 
    37 import Pyro.core as pyro
    36 import runtime
    38 
    37 from runtime.PyroServer import Server
    39 from runtime import PLCObject, ServicePublisher, MainWorker
       
    40 from runtime.xenomai import TryPreloadXenomai
    38 from runtime.xenomai import TryPreloadXenomai
       
    39 from runtime import LogMessageAndException
    41 import util.paths as paths
    40 import util.paths as paths
    42 
    41 
    43 
    42 
    44 def usage():
    43 def usage():
    45     print("""
    44     print("""
   240             TBMENU_LIVE_SHELL = wx.NewId()
   239             TBMENU_LIVE_SHELL = wx.NewId()
   241             TBMENU_WXINSPECTOR = wx.NewId()
   240             TBMENU_WXINSPECTOR = wx.NewId()
   242             TBMENU_CHANGE_WD = wx.NewId()
   241             TBMENU_CHANGE_WD = wx.NewId()
   243             TBMENU_QUIT = wx.NewId()
   242             TBMENU_QUIT = wx.NewId()
   244 
   243 
   245             def __init__(self, pyroserver, level):
   244             def __init__(self, pyroserver):
   246                 wx.TaskBarIcon.__init__(self)
   245                 wx.TaskBarIcon.__init__(self)
   247                 self.pyroserver = pyroserver
   246                 self.pyroserver = pyroserver
   248                 # Set the image
   247                 # Set the image
   249                 self.UpdateIcon(None)
   248                 self.UpdateIcon(None)
   250                 self.level = level
       
   251 
   249 
   252                 # bind some events
   250                 # bind some events
   253                 self.Bind(wx.EVT_MENU, self.OnTaskBarStartPLC, id=self.TBMENU_START)
   251                 self.Bind(wx.EVT_MENU, self.OnTaskBarStartPLC, id=self.TBMENU_START)
   254                 self.Bind(wx.EVT_MENU, self.OnTaskBarStopPLC, id=self.TBMENU_STOP)
   252                 self.Bind(wx.EVT_MENU, self.OnTaskBarStopPLC, id=self.TBMENU_STOP)
   255                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME)
   253                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME)
   268                 the base class takes care of the rest.
   266                 the base class takes care of the rest.
   269                 """
   267                 """
   270                 menu = wx.Menu()
   268                 menu = wx.Menu()
   271                 menu.Append(self.TBMENU_START, _("Start PLC"))
   269                 menu.Append(self.TBMENU_START, _("Start PLC"))
   272                 menu.Append(self.TBMENU_STOP, _("Stop PLC"))
   270                 menu.Append(self.TBMENU_STOP, _("Stop PLC"))
   273                 if self.level == 1:
   271                 menu.AppendSeparator()
   274                     menu.AppendSeparator()
   272                 menu.Append(self.TBMENU_CHANGE_NAME, _("Change Name"))
   275                     menu.Append(self.TBMENU_CHANGE_NAME, _("Change Name"))
   273                 menu.Append(self.TBMENU_CHANGE_INTERFACE, _("Change IP of interface to bind"))
   276                     menu.Append(self.TBMENU_CHANGE_INTERFACE, _("Change IP of interface to bind"))
   274                 menu.Append(self.TBMENU_CHANGE_PORT, _("Change Port Number"))
   277                     menu.Append(self.TBMENU_CHANGE_PORT, _("Change Port Number"))
   275                 menu.Append(self.TBMENU_CHANGE_WD, _("Change working directory"))
   278                     menu.Append(self.TBMENU_CHANGE_WD, _("Change working directory"))
   276                 menu.AppendSeparator()
   279                     menu.AppendSeparator()
   277                 menu.Append(self.TBMENU_LIVE_SHELL, _("Launch a live Python shell"))
   280                     menu.Append(self.TBMENU_LIVE_SHELL, _("Launch a live Python shell"))
   278                 menu.Append(self.TBMENU_WXINSPECTOR, _("Launch WX GUI inspector"))
   281                     menu.Append(self.TBMENU_WXINSPECTOR, _("Launch WX GUI inspector"))
       
   282                 menu.AppendSeparator()
   279                 menu.AppendSeparator()
   283                 menu.Append(self.TBMENU_QUIT, _("Quit"))
   280                 menu.Append(self.TBMENU_QUIT, _("Quit"))
   284                 return menu
   281                 return menu
   285 
   282 
   286             def MakeIcon(self, img):
   283             def MakeIcon(self, img):
   295                 # wxMac can be any size upto 128x128, so leave the source img alone....
   292                 # wxMac can be any size upto 128x128, so leave the source img alone....
   296                 icon = wx.IconFromBitmap(img.ConvertToBitmap())
   293                 icon = wx.IconFromBitmap(img.ConvertToBitmap())
   297                 return icon
   294                 return icon
   298 
   295 
   299             def OnTaskBarStartPLC(self, evt):
   296             def OnTaskBarStartPLC(self, evt):
   300                 if self.pyroserver.plcobj is not None:
   297                 runtime.GetPLCObjectSingleton().StartPLC()
   301                     plcstatus = self.pyroserver.plcobj.GetPLCstatus()[0]
       
   302                     if plcstatus is "Stopped":
       
   303                         self.pyroserver.plcobj.StartPLC()
       
   304                     else:
       
   305                         print(_("PLC is empty or already started."))
       
   306 
   298 
   307             def OnTaskBarStopPLC(self, evt):
   299             def OnTaskBarStopPLC(self, evt):
   308                 if self.pyroserver.plcobj is not None:
   300                 runtime.GetPLCObjectSingleton().StopPLC()
   309                     if self.pyroserver.plcobj.GetPLCstatus()[0] == "Started":
       
   310                         Thread(target=self.pyroserver.plcobj.StopPLC).start()
       
   311                     else:
       
   312                         print(_("PLC is not started."))
       
   313 
   301 
   314             def OnTaskBarChangeInterface(self, evt):
   302             def OnTaskBarChangeInterface(self, evt):
   315                 ip_addr = self.pyroserver.ip_addr
   303                 ip_addr = self.pyroserver.ip_addr
   316                 ip_addr = '' if ip_addr is None else ip_addr
   304                 ip_addr = '' if ip_addr is None else ip_addr
   317                 dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=ip_addr)
   305                 dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=ip_addr)
   343                 if dlg.ShowModal() == wx.ID_OK:
   331                 if dlg.ShowModal() == wx.ID_OK:
   344                     self.pyroserver.servicename = dlg.GetValue()
   332                     self.pyroserver.servicename = dlg.GetValue()
   345                     self.pyroserver.Restart()
   333                     self.pyroserver.Restart()
   346 
   334 
   347             def _LiveShellLocals(self):
   335             def _LiveShellLocals(self):
   348                 if self.pyroserver.plcobj is not None:
   336                 return {"locals": runtime.GetPLCObjectSingleton().python_runtime_vars}
   349                     return {"locals": self.pyroserver.plcobj.python_runtime_vars}
       
   350                 else:
       
   351                     return {}
       
   352 
   337 
   353             def OnTaskBarLiveShell(self, evt):
   338             def OnTaskBarLiveShell(self, evt):
   354                 from wx import py
   339                 from wx import py
   355                 frame = py.crust.CrustFrame(**self._LiveShellLocals())
   340                 frame = py.crust.CrustFrame(**self._LiveShellLocals())
   356                 frame.Show()
   341                 frame.Show()
   388     try:
   373     try:
   389         res = (tocall(*args, **kwargs), None)
   374         res = (tocall(*args, **kwargs), None)
   390     except Exception:
   375     except Exception:
   391         res = (None, sys.exc_info())
   376         res = (None, sys.exc_info())
   392     return res
   377     return res
   393 
       
   394 
       
   395 class Server(object):
       
   396     def __init__(self, servicename, ip_addr, port,
       
   397                  workdir, argv,
       
   398                  statuschange=None, evaluator=default_evaluator,
       
   399                  pyruntimevars=None):
       
   400         self.continueloop = True
       
   401         self.daemon = None
       
   402         self.servicename = servicename
       
   403         self.ip_addr = ip_addr
       
   404         self.port = port
       
   405         self.workdir = workdir
       
   406         self.argv = argv
       
   407         self.servicepublisher = None
       
   408         self.statuschange = statuschange
       
   409         self.evaluator = evaluator
       
   410         self.pyruntimevars = pyruntimevars
       
   411         self.plcobj = PLCObject(self)
       
   412 
       
   413     def _to_be_published(self):
       
   414         return self.servicename is not None and \
       
   415                self.ip_addr is not None and \
       
   416                self.ip_addr != "localhost" and \
       
   417                self.ip_addr != "127.0.0.1"
       
   418 
       
   419     def PrintServerInfo(self):
       
   420         print(_("Pyro port :"), self.port)
       
   421 
       
   422         # Beremiz IDE detects LOCAL:// runtime is ready by looking
       
   423         # for self.workdir in the daemon's stdout.
       
   424         print(_("Current working directory :"), self.workdir)
       
   425 
       
   426         if self._to_be_published():
       
   427             print(_("Publishing service on local network"))
       
   428 
       
   429         sys.stdout.flush()
       
   430 
       
   431     def PyroLoop(self, when_ready):
       
   432         while self.continueloop:
       
   433             Pyro.config.PYRO_MULTITHREADED = 0
       
   434             pyro.initServer()
       
   435             self.daemon = pyro.Daemon(host=self.ip_addr, port=self.port)
       
   436 
       
   437             # pyro never frees memory after connection close if no timeout set
       
   438             # taking too small timeout value may cause
       
   439             # unwanted diconnection when IDE is kept busy for long periods
       
   440             self.daemon.setTimeout(60)
       
   441 
       
   442             self.daemon.connect(self.plcobj, "PLCObject")
       
   443 
       
   444             if self._to_be_published():
       
   445                 self.servicepublisher = ServicePublisher.ServicePublisher()
       
   446                 self.servicepublisher.RegisterService(self.servicename, self.ip_addr, self.port)
       
   447 
       
   448             when_ready()
       
   449             self.daemon.requestLoop()
       
   450             self.daemon.sock.close()
       
   451 
       
   452     def Restart(self):
       
   453         self._stop()
       
   454 
       
   455     def Quit(self):
       
   456         self.continueloop = False
       
   457         if self.plcobj is not None:
       
   458             self.plcobj.StopPLC()
       
   459             self.plcobj.UnLoadPLC()
       
   460         self._stop()
       
   461 
       
   462     def _stop(self):
       
   463         if self.plcobj is not None:
       
   464             self.plcobj.StopPLC()
       
   465         if self.servicepublisher is not None:
       
   466             self.servicepublisher.UnRegisterService()
       
   467             self.servicepublisher = None
       
   468         self.daemon.shutdown(True)
       
   469 
       
   470     def AutoLoad(self):
       
   471         self.plcobj.AutoLoad()
       
   472         if self.plcobj.GetPLCstatus()[0] == "Stopped":
       
   473             if autostart:
       
   474                 self.plcobj.StartPLC()
       
   475         self.plcobj.StatusChange()
       
   476 
       
   477 
   378 
   478 if enabletwisted:
   379 if enabletwisted:
   479     import warnings
   380     import warnings
   480     with warnings.catch_warnings():
   381     with warnings.catch_warnings():
   481         warnings.simplefilter("ignore")
   382         warnings.simplefilter("ignore")
   520         # else:
   421         # else:
   521         o = type('', (object,), dict(call=(tocall, args, kwargs), res=None))
   422         o = type('', (object,), dict(call=(tocall, args, kwargs), res=None))
   522         wx.CallAfter(wx_evaluator, o)
   423         wx.CallAfter(wx_evaluator, o)
   523         wx_eval_lock.acquire()
   424         wx_eval_lock.acquire()
   524         return o.res
   425         return o.res
   525 
       
   526     pyroserver = Server(servicename, given_ip, port,
       
   527                         WorkingDir, argv,
       
   528                         statuschange, evaluator, pyruntimevars)
       
   529 
       
   530     taskbar_instance = BeremizTaskBarIcon(pyroserver, enablewx)
       
   531 else:
   426 else:
   532     pyroserver = Server(servicename, given_ip, port,
   427     evaluator = default_evaluator
   533                         WorkingDir, argv,
       
   534                         statuschange, pyruntimevars=pyruntimevars)
       
   535 
       
   536 
   428 
   537 # Exception hooks
   429 # Exception hooks
   538 
       
   539 
       
   540 def LogMessageAndException(msg, exp=None):
       
   541     if exp is None:
       
   542         exp = sys.exc_info()
       
   543     if pyroserver.plcobj is not None:
       
   544         pyroserver.plcobj.LogMessage(0, msg + '\n'.join(traceback.format_exception(*exp)))
       
   545     else:
       
   546         print(msg)
       
   547         traceback.print_exception(*exp)
       
   548 
       
   549 
   430 
   550 def LogException(*exp):
   431 def LogException(*exp):
   551     LogMessageAndException("", exp)
   432     LogMessageAndException("", exp)
   552 
   433 
   553 
   434 
   594 # Load extensions
   475 # Load extensions
   595 for extention_file, extension_folder in extensions:
   476 for extention_file, extension_folder in extensions:
   596     sys.path.append(extension_folder)
   477     sys.path.append(extension_folder)
   597     execfile(os.path.join(extension_folder, extention_file), locals())
   478     execfile(os.path.join(extension_folder, extention_file), locals())
   598 
   479 
       
   480 
       
   481 runtime.CreatePLCObjectSingleton(
       
   482     WorkingDir, argv, statuschange, evaluator, pyruntimevars)
       
   483 
       
   484 pyroserver = Server(servicename, given_ip, port)
       
   485 
       
   486 if havewx:
       
   487     taskbar_instance = BeremizTaskBarIcon(pyroserver)
       
   488 
   599 if havetwisted:
   489 if havetwisted:
   600     if webport is not None:
   490     if webport is not None:
   601         try:
   491         try:
   602             website = NS.RegisterWebsite(webport)
   492             website = NS.RegisterWebsite(webport)
   603             pyruntimevars["website"] = website
   493             pyruntimevars["website"] = website
   622 
   512 
   623 # Wait for pyro thread to be effective
   513 # Wait for pyro thread to be effective
   624 pyro_thread_started.acquire()
   514 pyro_thread_started.acquire()
   625 
   515 
   626 pyroserver.PrintServerInfo()
   516 pyroserver.PrintServerInfo()
       
   517 
       
   518 # Beremiz IDE detects LOCAL:// runtime is ready by looking
       
   519 # for self.workdir in the daemon's stdout.
       
   520 print(_("Current working directory :"), WorkingDir)
       
   521 
   627 
   522 
   628 if havetwisted or havewx:
   523 if havetwisted or havewx:
   629     ui_thread_started = Lock()
   524     ui_thread_started = Lock()
   630     ui_thread_started.acquire()
   525     ui_thread_started.acquire()
   631     if havetwisted:
   526     if havetwisted:
   649     # Wait for ui thread to be effective
   544     # Wait for ui thread to be effective
   650     ui_thread_started.acquire()
   545     ui_thread_started.acquire()
   651     print("UI thread started successfully.")
   546     print("UI thread started successfully.")
   652 
   547 
   653 try:
   548 try:
   654     MainWorker.runloop(pyroserver.AutoLoad)
   549     runtime.MainWorker.runloop(
       
   550         runtime.GetPLCObjectSingleton().AutoLoad, autostart)
   655 except KeyboardInterrupt:
   551 except KeyboardInterrupt:
   656     pass
   552     pass
   657 
   553 
   658 pyroserver.Quit()
   554 pyroserver.Quit()
       
   555 
       
   556 plcobj = runtime.GetPLCObjectSingleton()
       
   557 plcobj.StopPLC()
       
   558 plcobj.UnLoadPLC()
       
   559 
   659 sys.exit(0)
   560 sys.exit(0)