Beremiz_service.py
changeset 1434 6e0cd0ceabb7
parent 1270 aa9bc3e6181d
child 1437 04177743b066
equal deleted inserted replaced
1433:4a45f6642523 1434:6e0cd0ceabb7
     1 #!/usr/bin/env python
     1 #!/usr/bin/env python
     2 # -*- coding: utf-8 -*-
     2 # -*- coding: utf-8 -*-
     3 
     3 
     4 #This file is part of Beremiz, a Integrated Development Environment for
     4 #This file is part of Beremiz, a Integrated Development Environment for
     5 #programming IEC 61131-3 automates supporting plcopen standard and CanFestival. 
     5 #programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
     6 #
     6 #
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
     8 #
     8 #
     9 #See COPYING file for copyrights details.
     9 #See COPYING file for copyrights details.
    10 #
    10 #
    34            -p        - port number default:3000
    34            -p        - port number default:3000
    35            -h        - print this help text and quit
    35            -h        - print this help text and quit
    36            -a        - autostart PLC (0:disable 1:enable) (default:0)
    36            -a        - autostart PLC (0:disable 1:enable) (default:0)
    37            -x        - enable/disable wxTaskbarIcon (0:disable 1:enable) (default:1)
    37            -x        - enable/disable wxTaskbarIcon (0:disable 1:enable) (default:1)
    38            -t        - enable/disable Twisted web interface (0:disable 1:enable) (default:1)
    38            -t        - enable/disable Twisted web interface (0:disable 1:enable) (default:1)
    39            
    39 
    40            working_dir - directory where are stored PLC files
    40            working_dir - directory where are stored PLC files
    41 """%sys.argv[0]
    41 """%sys.argv[0]
    42 
    42 
    43 try:
    43 try:
    44     opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:t:a:h")
    44     opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:t:a:h")
    81         autostart = int(a)
    81         autostart = int(a)
    82     else:
    82     else:
    83         usage()
    83         usage()
    84         sys.exit()
    84         sys.exit()
    85 
    85 
       
    86 CWD = os.path.split(os.path.realpath(__file__))[0]
       
    87 
    86 if len(argv) > 1:
    88 if len(argv) > 1:
    87     usage()
    89     usage()
    88     sys.exit()
    90     sys.exit()
    89 elif len(argv) == 1:
    91 elif len(argv) == 1:
    90     WorkingDir = argv[0]
    92     WorkingDir = argv[0]
   107         print "Wx unavailable !"
   109         print "Wx unavailable !"
   108         havewx = False
   110         havewx = False
   109 
   111 
   110     if havewx:
   112     if havewx:
   111         app=wx.App(redirect=False)
   113         app=wx.App(redirect=False)
   112         
   114 
   113         # Import module for internationalization
   115         # Import module for internationalization
   114         import gettext
   116         import gettext
   115         
   117 
   116         CWD = os.path.split(os.path.realpath(__file__))[0]
       
   117         def Bpath(*args):
   118         def Bpath(*args):
   118             return os.path.join(CWD,*args)
   119             return os.path.join(CWD,*args)
   119         
   120 
   120         # Get folder containing translation files
   121         # Get folder containing translation files
   121         localedir = os.path.join(CWD,"locale")
   122         localedir = os.path.join(CWD,"locale")
   122         # Get the default language
   123         # Get the default language
   123         langid = wx.LANGUAGE_DEFAULT
   124         langid = wx.LANGUAGE_DEFAULT
   124         # Define translation domain (name of translation files)
   125         # Define translation domain (name of translation files)
   137         def unicode_translation(message):
   138         def unicode_translation(message):
   138             return wx.GetTranslation(message).encode("utf-8")
   139             return wx.GetTranslation(message).encode("utf-8")
   139 
   140 
   140         if __name__ == '__main__':
   141         if __name__ == '__main__':
   141             __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
   142             __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
   142         
   143 
   143         defaulticon = wx.Image(Bpath("images", "brz.png"))
   144         defaulticon = wx.Image(Bpath("images", "brz.png"))
   144         starticon = wx.Image(Bpath("images", "icoplay24.png"))
   145         starticon = wx.Image(Bpath("images", "icoplay24.png"))
   145         stopicon = wx.Image(Bpath("images", "icostop24.png"))
   146         stopicon = wx.Image(Bpath("images", "icostop24.png"))
   146         
   147 
   147         class ParamsEntryDialog(wx.TextEntryDialog):
   148         class ParamsEntryDialog(wx.TextEntryDialog):
   148             if wx.VERSION < (2, 6, 0):
   149             if wx.VERSION < (2, 6, 0):
   149                 def Bind(self, event, function, id = None):
   150                 def Bind(self, event, function, id = None):
   150                     if id is not None:
   151                     if id is not None:
   151                         event(self, id, function)
   152                         event(self, id, function)
   152                     else:
   153                     else:
   153                         event(self, function)
   154                         event(self, function)
   154             
   155 
   155             
   156 
   156             def __init__(self, parent, message, caption = "Please enter text", defaultValue = "", 
   157             def __init__(self, parent, message, caption = "Please enter text", defaultValue = "",
   157                                style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
   158                                style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
   158                 wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
   159                 wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
   159                 
   160 
   160                 self.Tests = []
   161                 self.Tests = []
   161                 if wx.VERSION >= (2, 8, 0):
   162                 if wx.VERSION >= (2, 8, 0):
   162                     self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId())
   163                     self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId())
   163                 elif wx.VERSION >= (2, 6, 0):
   164                 elif wx.VERSION >= (2, 6, 0):
   164                     self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId())
   165                     self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId())
   165                 else:
   166                 else:
   166                     self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
   167                     self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
   167             
   168 
   168             def OnOK(self, event):
   169             def OnOK(self, event):
   169                 value = self.GetValue()
   170                 value = self.GetValue()
   170                 texts = {"value" : value}
   171                 texts = {"value" : value}
   171                 for function, message in self.Tests:
   172                 for function, message in self.Tests:
   172                     if not function(value):
   173                     if not function(value):
   174                         message.ShowModal()
   175                         message.ShowModal()
   175                         message.Destroy()
   176                         message.Destroy()
   176                         return
   177                         return
   177                 self.EndModal(wx.ID_OK)
   178                 self.EndModal(wx.ID_OK)
   178                 event.Skip()
   179                 event.Skip()
   179             
   180 
   180             def GetValue(self):
   181             def GetValue(self):
   181                 return self.GetSizer().GetItem(1).GetWindow().GetValue()
   182                 return self.GetSizer().GetItem(1).GetWindow().GetValue()
   182             
   183 
   183             def SetTests(self, tests):
   184             def SetTests(self, tests):
   184                 self.Tests = tests
   185                 self.Tests = tests
   185         
   186 
   186         class BeremizTaskBarIcon(wx.TaskBarIcon):
   187         class BeremizTaskBarIcon(wx.TaskBarIcon):
   187             TBMENU_START = wx.NewId()
   188             TBMENU_START = wx.NewId()
   188             TBMENU_STOP = wx.NewId()
   189             TBMENU_STOP = wx.NewId()
   189             TBMENU_CHANGE_NAME = wx.NewId()
   190             TBMENU_CHANGE_NAME = wx.NewId()
   190             TBMENU_CHANGE_PORT = wx.NewId()
   191             TBMENU_CHANGE_PORT = wx.NewId()
   191             TBMENU_CHANGE_INTERFACE = wx.NewId()
   192             TBMENU_CHANGE_INTERFACE = wx.NewId()
   192             TBMENU_LIVE_SHELL = wx.NewId()
   193             TBMENU_LIVE_SHELL = wx.NewId()
   193             TBMENU_WXINSPECTOR = wx.NewId()
   194             TBMENU_WXINSPECTOR = wx.NewId()
   194             TBMENU_CHANGE_WD = wx.NewId()
   195             TBMENU_CHANGE_WD = wx.NewId()
   195             TBMENU_QUIT = wx.NewId()
   196             TBMENU_QUIT = wx.NewId()
   196             
   197 
   197             def __init__(self, pyroserver, level):
   198             def __init__(self, pyroserver, level):
   198                 wx.TaskBarIcon.__init__(self)
   199                 wx.TaskBarIcon.__init__(self)
   199                 self.pyroserver = pyroserver
   200                 self.pyroserver = pyroserver
   200                 # Set the image
   201                 # Set the image
   201                 self.UpdateIcon(None)
   202                 self.UpdateIcon(None)
   202                 self.level = level
   203                 self.level = level
   203                 
   204 
   204                 # bind some events
   205                 # bind some events
   205                 self.Bind(wx.EVT_MENU, self.OnTaskBarStartPLC, id=self.TBMENU_START)
   206                 self.Bind(wx.EVT_MENU, self.OnTaskBarStartPLC, id=self.TBMENU_START)
   206                 self.Bind(wx.EVT_MENU, self.OnTaskBarStopPLC, id=self.TBMENU_STOP)
   207                 self.Bind(wx.EVT_MENU, self.OnTaskBarStopPLC, id=self.TBMENU_STOP)
   207                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME)
   208                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME)
   208                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangeInterface, id=self.TBMENU_CHANGE_INTERFACE)
   209                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangeInterface, id=self.TBMENU_CHANGE_INTERFACE)
   209                 self.Bind(wx.EVT_MENU, self.OnTaskBarLiveShell, id=self.TBMENU_LIVE_SHELL)
   210                 self.Bind(wx.EVT_MENU, self.OnTaskBarLiveShell, id=self.TBMENU_LIVE_SHELL)
   210                 self.Bind(wx.EVT_MENU, self.OnTaskBarWXInspector, id=self.TBMENU_WXINSPECTOR)
   211                 self.Bind(wx.EVT_MENU, self.OnTaskBarWXInspector, id=self.TBMENU_WXINSPECTOR)
   211                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangePort, id=self.TBMENU_CHANGE_PORT)
   212                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangePort, id=self.TBMENU_CHANGE_PORT)
   212                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangeWorkingDir, id=self.TBMENU_CHANGE_WD)
   213                 self.Bind(wx.EVT_MENU, self.OnTaskBarChangeWorkingDir, id=self.TBMENU_CHANGE_WD)
   213                 self.Bind(wx.EVT_MENU, self.OnTaskBarQuit, id=self.TBMENU_QUIT)
   214                 self.Bind(wx.EVT_MENU, self.OnTaskBarQuit, id=self.TBMENU_QUIT)
   214             
   215 
   215             def CreatePopupMenu(self):
   216             def CreatePopupMenu(self):
   216                 """
   217                 """
   217                 This method is called by the base class when it needs to popup
   218                 This method is called by the base class when it needs to popup
   218                 the menu for the default EVT_RIGHT_DOWN event.  Just create
   219                 the menu for the default EVT_RIGHT_DOWN event.  Just create
   219                 the menu how you want it and return it from this function,
   220                 the menu how you want it and return it from this function,
   232                     menu.Append(self.TBMENU_LIVE_SHELL, _("Launch a live Python shell"))
   233                     menu.Append(self.TBMENU_LIVE_SHELL, _("Launch a live Python shell"))
   233                     menu.Append(self.TBMENU_WXINSPECTOR, _("Launch WX GUI inspector"))
   234                     menu.Append(self.TBMENU_WXINSPECTOR, _("Launch WX GUI inspector"))
   234                 menu.AppendSeparator()
   235                 menu.AppendSeparator()
   235                 menu.Append(self.TBMENU_QUIT, _("Quit"))
   236                 menu.Append(self.TBMENU_QUIT, _("Quit"))
   236                 return menu
   237                 return menu
   237             
   238 
   238             def MakeIcon(self, img):
   239             def MakeIcon(self, img):
   239                 """
   240                 """
   240                 The various platforms have different requirements for the
   241                 The various platforms have different requirements for the
   241                 icon size...
   242                 icon size...
   242                 """
   243                 """
   245                 elif "wxGTK" in wx.PlatformInfo:
   246                 elif "wxGTK" in wx.PlatformInfo:
   246                     img = img.Scale(22, 22)
   247                     img = img.Scale(22, 22)
   247                 # wxMac can be any size upto 128x128, so leave the source img alone....
   248                 # wxMac can be any size upto 128x128, so leave the source img alone....
   248                 icon = wx.IconFromBitmap(img.ConvertToBitmap() )
   249                 icon = wx.IconFromBitmap(img.ConvertToBitmap() )
   249                 return icon
   250                 return icon
   250             
   251 
   251             def OnTaskBarStartPLC(self, evt):
   252             def OnTaskBarStartPLC(self, evt):
   252                 if self.pyroserver.plcobj is not None: 
   253                 if self.pyroserver.plcobj is not None:
   253                     self.pyroserver.plcobj.StartPLC()
   254                     self.pyroserver.plcobj.StartPLC()
   254             
   255 
   255             def OnTaskBarStopPLC(self, evt):
   256             def OnTaskBarStopPLC(self, evt):
   256                 if self.pyroserver.plcobj is not None:
   257                 if self.pyroserver.plcobj is not None:
   257                     Thread(target=self.pyroserver.plcobj.StopPLC).start()
   258                     Thread(target=self.pyroserver.plcobj.StopPLC).start()
   258             
   259 
   259             def OnTaskBarChangeInterface(self, evt):
   260             def OnTaskBarChangeInterface(self, evt):
   260                 dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=self.pyroserver.ip_addr)
   261                 dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=self.pyroserver.ip_addr)
   261                 dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, _("IP is not valid!")),
   262                 dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, _("IP is not valid!")),
   262                                ( lambda x :len([x for x in x.split(".") if 0 <= int(x) <= 255]) == 4, _("IP is not valid!"))
   263                                ( lambda x :len([x for x in x.split(".") if 0 <= int(x) <= 255]) == 4, _("IP is not valid!"))
   263                                ])
   264                                ])
   264                 if dlg.ShowModal() == wx.ID_OK:
   265                 if dlg.ShowModal() == wx.ID_OK:
   265                     self.pyroserver.ip_addr = dlg.GetValue()
   266                     self.pyroserver.ip_addr = dlg.GetValue()
   266                     self.pyroserver.Stop()
   267                     self.pyroserver.Stop()
   267             
   268 
   268             def OnTaskBarChangePort(self, evt):
   269             def OnTaskBarChangePort(self, evt):
   269                 dlg = ParamsEntryDialog(None, _("Enter a port number "), defaultValue=str(self.pyroserver.port))
   270                 dlg = ParamsEntryDialog(None, _("Enter a port number "), defaultValue=str(self.pyroserver.port))
   270                 dlg.SetTests([(UnicodeType.isdigit, _("Port number must be an integer!")), (lambda port : 0 <= int(port) <= 65535 , _("Port number must be 0 <= port <= 65535!"))])
   271                 dlg.SetTests([(UnicodeType.isdigit, _("Port number must be an integer!")), (lambda port : 0 <= int(port) <= 65535 , _("Port number must be 0 <= port <= 65535!"))])
   271                 if dlg.ShowModal() == wx.ID_OK:
   272                 if dlg.ShowModal() == wx.ID_OK:
   272                     self.pyroserver.port = int(dlg.GetValue())
   273                     self.pyroserver.port = int(dlg.GetValue())
   273                     self.pyroserver.Stop()
   274                     self.pyroserver.Stop()
   274             
   275 
   275             def OnTaskBarChangeWorkingDir(self, evt):
   276             def OnTaskBarChangeWorkingDir(self, evt):
   276                 dlg = wx.DirDialog(None, _("Choose a working directory "), self.pyroserver.workdir, wx.DD_NEW_DIR_BUTTON)
   277                 dlg = wx.DirDialog(None, _("Choose a working directory "), self.pyroserver.workdir, wx.DD_NEW_DIR_BUTTON)
   277                 if dlg.ShowModal() == wx.ID_OK:
   278                 if dlg.ShowModal() == wx.ID_OK:
   278                     self.pyroserver.workdir = dlg.GetPath()
   279                     self.pyroserver.workdir = dlg.GetPath()
   279                     self.pyroserver.Stop()
   280                     self.pyroserver.Stop()
   280             
   281 
   281             def OnTaskBarChangeName(self, evt):
   282             def OnTaskBarChangeName(self, evt):
   282                 dlg = ParamsEntryDialog(None, _("Enter a name "), defaultValue=self.pyroserver.name)
   283                 dlg = ParamsEntryDialog(None, _("Enter a name "), defaultValue=self.pyroserver.name)
   283                 dlg.SetTests([(lambda name : len(name) is not 0 , _("Name must not be null!"))])
   284                 dlg.SetTests([(lambda name : len(name) is not 0 , _("Name must not be null!"))])
   284                 if dlg.ShowModal() == wx.ID_OK:
   285                 if dlg.ShowModal() == wx.ID_OK:
   285                     self.pyroserver.name = dlg.GetValue()
   286                     self.pyroserver.name = dlg.GetValue()
   286                     self.pyroserver.Restart()
   287                     self.pyroserver.Restart()
   287             
   288 
   288             def _LiveShellLocals(self):
   289             def _LiveShellLocals(self):
   289                 if self.pyroserver.plcobj is not None:
   290                 if self.pyroserver.plcobj is not None:
   290                     return {"locals":self.pyroserver.plcobj.python_runtime_vars}
   291                     return {"locals":self.pyroserver.plcobj.python_runtime_vars}
   291                 else:
   292                 else:
   292                     return {}
   293                     return {}
   293             
   294 
   294             def OnTaskBarLiveShell(self, evt):
   295             def OnTaskBarLiveShell(self, evt):
   295                 from wx import py
   296                 from wx import py
   296                 frame = py.crust.CrustFrame(**self._LiveShellLocals())
   297                 frame = py.crust.CrustFrame(**self._LiveShellLocals())
   297                 frame.Show()
   298                 frame.Show()
   298             
   299 
   299             def OnTaskBarWXInspector(self, evt):
   300             def OnTaskBarWXInspector(self, evt):
   300                 # Activate the widget inspection tool
   301                 # Activate the widget inspection tool
   301                 from wx.lib.inspection import InspectionTool
   302                 from wx.lib.inspection import InspectionTool
   302                 if not InspectionTool().initialized:
   303                 if not InspectionTool().initialized:
   303                     InspectionTool().Init(**self._LiveShellLocals())
   304                     InspectionTool().Init(**self._LiveShellLocals())
   304 
   305 
   305                 wnd = wx.GetApp()
   306                 wnd = wx.GetApp()
   306                 InspectionTool().Show(wnd, True)
   307                 InspectionTool().Show(wnd, True)
   307             
   308 
   308             def OnTaskBarQuit(self, evt):
   309             def OnTaskBarQuit(self, evt):
   309                 if wx.Platform == '__WXMSW__':
   310                 if wx.Platform == '__WXMSW__':
   310                     Thread(target=self.pyroserver.Quit).start()
   311                     Thread(target=self.pyroserver.Quit).start()
   311                 self.RemoveIcon()
   312                 self.RemoveIcon()
   312                 wx.CallAfter(wx.GetApp().ExitMainLoop)
   313                 wx.CallAfter(wx.GetApp().ExitMainLoop)
   313             
   314 
   314             def UpdateIcon(self, plcstatus):
   315             def UpdateIcon(self, plcstatus):
   315                 if plcstatus is "Started" :
   316                 if plcstatus is "Started" :
   316                     currenticon = self.MakeIcon(starticon)
   317                     currenticon = self.MakeIcon(starticon)
   317                 elif plcstatus is "Stopped":
   318                 elif plcstatus is "Stopped":
   318                     currenticon = self.MakeIcon(stopicon)
   319                     currenticon = self.MakeIcon(stopicon)
   346         self.servicepublisher = None
   347         self.servicepublisher = None
   347         self.autostart = autostart
   348         self.autostart = autostart
   348         self.statuschange = statuschange
   349         self.statuschange = statuschange
   349         self.evaluator = evaluator
   350         self.evaluator = evaluator
   350         self.website = website
   351         self.website = website
   351     
   352 
   352     def Loop(self):
   353     def Loop(self):
   353         while self.continueloop:
   354         while self.continueloop:
   354             self.Start()
   355             self.Start()
   355         
   356 
   356     def Restart(self):
   357     def Restart(self):
   357         self.Stop()
   358         self.Stop()
   358 
   359 
   359     def Quit(self):
   360     def Quit(self):
   360         self.continueloop = False
   361         self.continueloop = False
   365     def Start(self):
   366     def Start(self):
   366         pyro.initServer()
   367         pyro.initServer()
   367         self.daemon=pyro.Daemon(host=self.ip_addr, port=self.port)
   368         self.daemon=pyro.Daemon(host=self.ip_addr, port=self.port)
   368         self.plcobj = PLCObject(self.workdir, self.daemon, self.argv, self.statuschange, self.evaluator, self.website)
   369         self.plcobj = PLCObject(self.workdir, self.daemon, self.argv, self.statuschange, self.evaluator, self.website)
   369         uri = self.daemon.connect(self.plcobj,"PLCObject")
   370         uri = self.daemon.connect(self.plcobj,"PLCObject")
   370     
   371 
   371         print "Pyro port :",self.port
   372         print "Pyro port :",self.port
   372         print "Pyro object's uri :",uri
   373         print "Pyro object's uri :",uri
   373         print "Current working directory :",self.workdir
   374         print "Current working directory :",self.workdir
   374         
   375 
   375         # Configure and publish service
   376         # Configure and publish service
   376         # Not publish service if localhost in address params
   377         # Not publish service if localhost in address params
   377         if (self.servicename is not None and 
   378         if (self.servicename is not None and
   378             self.ip_addr is not None and 
   379             self.ip_addr is not None and
   379             self.ip_addr != "localhost" and 
   380             self.ip_addr != "localhost" and
   380             self.ip_addr != "127.0.0.1"):
   381             self.ip_addr != "127.0.0.1"):
   381             print "Publishing service on local network"
   382             print "Publishing service on local network"
   382             self.servicepublisher = ServicePublisher.ServicePublisher()
   383             self.servicepublisher = ServicePublisher.ServicePublisher()
   383             self.servicepublisher.RegisterService(self.servicename, self.ip_addr, self.port)
   384             self.servicepublisher.RegisterService(self.servicename, self.ip_addr, self.port)
   384         
   385 
   385         if self.autostart and self.plcobj.GetPLCstatus()[0] != "Empty":
   386         if self.autostart and self.plcobj.GetPLCstatus()[0] != "Empty":
   386             self.plcobj.StartPLC()
   387             self.plcobj.StartPLC()
   387         
   388 
   388         sys.stdout.flush()
   389         sys.stdout.flush()
   389         
   390 
   390         self.daemon.requestLoop()
   391         self.daemon.requestLoop()
   391     
   392 
   392     def Stop(self):
   393     def Stop(self):
   393         if self.plcobj is not None:
   394         if self.plcobj is not None:
   394             self.plcobj.StopPLC()
   395             self.plcobj.StopPLC()
   395         if self.servicepublisher is not None:
   396         if self.servicepublisher is not None:
   396             self.servicepublisher.UnRegisterService()
   397             self.servicepublisher.UnRegisterService()
   408                 wxreactor.install()
   409                 wxreactor.install()
   409             from twisted.internet import reactor, task
   410             from twisted.internet import reactor, task
   410             from twisted.python import log, util
   411             from twisted.python import log, util
   411             from nevow import rend, appserver, inevow, tags, loaders, athena
   412             from nevow import rend, appserver, inevow, tags, loaders, athena
   412             from nevow.page import renderer
   413             from nevow.page import renderer
   413             
   414 
   414             havetwisted = True
   415             havetwisted = True
   415         except:
   416         except:
   416             print "Twisted unavailable !"
   417             print "Twisted unavailable !"
   417             havetwisted = False
   418             havetwisted = False
   418 
   419 
   419 if havetwisted:
   420 if havetwisted:
   420     
   421 
   421     xhtml_header = '''<?xml version="1.0" encoding="utf-8"?>
   422     xhtml_header = '''<?xml version="1.0" encoding="utf-8"?>
   422 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   423 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   423 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
   424 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
   424 '''
   425 '''
   425 
   426 
   426     class PLCHMI(athena.LiveElement):
   427     class PLCHMI(athena.LiveElement):
   427     
   428 
   428         initialised = False
   429         initialised = False
   429     
   430 
   430         def HMIinitialised(self, result):
   431         def HMIinitialised(self, result):
   431             self.initialised = True
   432             self.initialised = True
   432         
   433 
   433         def HMIinitialisation(self):
   434         def HMIinitialisation(self):
   434             self.HMIinitialised(None)
   435             self.HMIinitialised(None)
   435     
   436 
   436     class DefaultPLCStartedHMI(PLCHMI):
   437     class DefaultPLCStartedHMI(PLCHMI):
   437         docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[                                    
   438         docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
   438                                              tags.h1["PLC IS NOW STARTED"],
   439                                              tags.h1["PLC IS NOW STARTED"],
   439                                              ])
   440                                              ])
   440         
   441 
   441     class PLCStoppedHMI(PLCHMI):
   442     class PLCStoppedHMI(PLCHMI):
   442         docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
   443         docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
   443                                              tags.h1["PLC IS STOPPED"],
   444                                              tags.h1["PLC IS STOPPED"],
   444                                              ])
   445                                              ])
   445     
   446 
   446     class MainPage(athena.LiveElement):
   447     class MainPage(athena.LiveElement):
   447         jsClass = u"WebInterface.PLC"
   448         jsClass = u"WebInterface.PLC"
   448         docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
   449         docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
   449                                                         tags.div(id='content')[                         
   450                                                         tags.div(id='content')[
   450                                                         tags.div(render = tags.directive('PLCElement')),
   451                                                         tags.div(render = tags.directive('PLCElement')),
   451                                                         ]])
   452                                                         ]])
   452         
   453 
   453         def __init__(self, *a, **kw):
   454         def __init__(self, *a, **kw):
   454             athena.LiveElement.__init__(self, *a, **kw)
   455             athena.LiveElement.__init__(self, *a, **kw)
   455             self.pcl_state = False
   456             self.pcl_state = False
   456             self.HMI = None
   457             self.HMI = None
   457             self.resetPLCStartedHMI()
   458             self.resetPLCStartedHMI()
   458         
   459 
   459         def setPLCState(self, state):
   460         def setPLCState(self, state):
   460             self.pcl_state = state
   461             self.pcl_state = state
   461             if self.HMI is not None:
   462             if self.HMI is not None:
   462                 self.callRemote('updateHMI')
   463                 self.callRemote('updateHMI')
   463         
   464 
   464         def setPLCStartedHMI(self, hmi):
   465         def setPLCStartedHMI(self, hmi):
   465             self.PLCStartedHMIClass = hmi
   466             self.PLCStartedHMIClass = hmi
   466         
   467 
   467         def resetPLCStartedHMI(self):
   468         def resetPLCStartedHMI(self):
   468             self.PLCStartedHMIClass = DefaultPLCStartedHMI
   469             self.PLCStartedHMIClass = DefaultPLCStartedHMI
   469         
   470 
   470         def getHMI(self):
   471         def getHMI(self):
   471             return self.HMI
   472             return self.HMI
   472         
   473 
   473         def HMIexec(self, function, *args, **kwargs):
   474         def HMIexec(self, function, *args, **kwargs):
   474             if self.HMI is not None:
   475             if self.HMI is not None:
   475                 getattr(self.HMI, function, lambda:None)(*args, **kwargs)
   476                 getattr(self.HMI, function, lambda:None)(*args, **kwargs)
   476         athena.expose(HMIexec)
   477         athena.expose(HMIexec)
   477         
   478 
   478         def resetHMI(self):
   479         def resetHMI(self):
   479             self.HMI = None
   480             self.HMI = None
   480         
   481 
   481         def PLCElement(self, ctx, data):
   482         def PLCElement(self, ctx, data):
   482             return self.getPLCElement()
   483             return self.getPLCElement()
   483         renderer(PLCElement)
   484         renderer(PLCElement)
   484         
   485 
   485         def getPLCElement(self):
   486         def getPLCElement(self):
   486             self.detachFragmentChildren()
   487             self.detachFragmentChildren()
   487             if self.pcl_state:
   488             if self.pcl_state:
   488                 f = self.PLCStartedHMIClass()
   489                 f = self.PLCStartedHMIClass()
   489             else:
   490             else:
   494         athena.expose(getPLCElement)
   495         athena.expose(getPLCElement)
   495 
   496 
   496         def detachFragmentChildren(self):
   497         def detachFragmentChildren(self):
   497             for child in self.liveFragmentChildren[:]:
   498             for child in self.liveFragmentChildren[:]:
   498                 child.detach()
   499                 child.detach()
   499     
   500 
   500     class WebInterface(athena.LivePage):
   501     class WebInterface(athena.LivePage):
   501 
   502 
   502         docFactory = loaders.stan([tags.raw(xhtml_header),
   503         docFactory = loaders.stan([tags.raw(xhtml_header),
   503                                    tags.html(xmlns="http://www.w3.org/1999/xhtml")[
   504                                    tags.html(xmlns="http://www.w3.org/1999/xhtml")[
   504                                        tags.head(render=tags.directive('liveglue')),
   505                                        tags.head(render=tags.directive('liveglue')),
   506                                            tags.div[
   507                                            tags.div[
   507                                                    tags.div( render = tags.directive( "MainPage" ))
   508                                                    tags.div( render = tags.directive( "MainPage" ))
   508                                                    ]]]])
   509                                                    ]]]])
   509         MainPage = MainPage()
   510         MainPage = MainPage()
   510         PLCHMI = PLCHMI
   511         PLCHMI = PLCHMI
   511         
   512 
   512         def __init__(self, plcState=False, *a, **kw):
   513         def __init__(self, plcState=False, *a, **kw):
   513             super(WebInterface, self).__init__(*a, **kw)
   514             super(WebInterface, self).__init__(*a, **kw)
   514             self.jsModules.mapping[u'WebInterface'] = util.sibpath(__file__, os.path.join('runtime', 'webinterface.js'))
   515             self.jsModules.mapping[u'WebInterface'] = util.sibpath(__file__, os.path.join('runtime', 'webinterface.js'))
   515             self.plcState = plcState
   516             self.plcState = plcState
   516             self.MainPage.setPLCState(plcState)
   517             self.MainPage.setPLCState(plcState)
   517 
   518 
   518         def getHMI(self):
   519         def getHMI(self):
   519             return self.MainPage.getHMI()
   520             return self.MainPage.getHMI()
   520         
   521 
   521         def LoadHMI(self, hmi, jsmodules):
   522         def LoadHMI(self, hmi, jsmodules):
   522             for name, path in jsmodules.iteritems():
   523             for name, path in jsmodules.iteritems():
   523                 self.jsModules.mapping[name] = os.path.join(WorkingDir, path)
   524                 self.jsModules.mapping[name] = os.path.join(WorkingDir, path)
   524             self.MainPage.setPLCStartedHMI(hmi)
   525             self.MainPage.setPLCStartedHMI(hmi)
   525         
   526 
   526         def UnLoadHMI(self):
   527         def UnLoadHMI(self):
   527             self.MainPage.resetPLCStartedHMI()
   528             self.MainPage.resetPLCStartedHMI()
   528         
   529 
   529         def PLCStarted(self):
   530         def PLCStarted(self):
   530             self.plcState = True
   531             self.plcState = True
   531             self.MainPage.setPLCState(True)
   532             self.MainPage.setPLCState(True)
   532         
   533 
   533         def PLCStopped(self):
   534         def PLCStopped(self):
   534             self.plcState = False
   535             self.plcState = False
   535             self.MainPage.setPLCState(False)
   536             self.MainPage.setPLCState(False)
   536             
   537 
   537         def renderHTTP(self, ctx):
   538         def renderHTTP(self, ctx):
   538             """
   539             """
   539             Force content type to fit with SVG
   540             Force content type to fit with SVG
   540             """
   541             """
   541             req = inevow.IRequest(ctx)
   542             req = inevow.IRequest(ctx)
   548             return ctx.tag[f]
   549             return ctx.tag[f]
   549 
   550 
   550         def child_(self, ctx):
   551         def child_(self, ctx):
   551             self.MainPage.detachFragmentChildren()
   552             self.MainPage.detachFragmentChildren()
   552             return WebInterface(plcState=self.plcState)
   553             return WebInterface(plcState=self.plcState)
   553             
   554 
   554         def beforeRender(self, ctx):
   555         def beforeRender(self, ctx):
   555             d = self.notifyOnDisconnect()
   556             d = self.notifyOnDisconnect()
   556             d.addErrback(self.disconnected)
   557             d.addErrback(self.disconnected)
   557         
   558 
   558         def disconnected(self, reason):
   559         def disconnected(self, reason):
   559             self.MainPage.resetHMI()
   560             self.MainPage.resetHMI()
   560             #print reason
   561             #print reason
   561             #print "We will be called back when the client disconnects"
   562             #print "We will be called back when the client disconnects"
   562         
   563 
   563     if havewx:
   564     if havewx:
   564         reactor.registerWxApp(app)
   565         reactor.registerWxApp(app)
   565     website = WebInterface()
   566     website = WebInterface()
   566     site = appserver.NevowSite(website)
   567     site = appserver.NevowSite(website)
   567     
   568 
   568     website_port = 8009
   569     website_port = 8009
   569     listening = False
   570     listening = False
   570     while not listening:
   571     while not listening:
   571         try:
   572         try:
   572             reactor.listenTCP(website_port, site)
   573             reactor.listenTCP(website_port, site)
   582     wx_eval_lock = Semaphore(0)
   583     wx_eval_lock = Semaphore(0)
   583     main_thread = currentThread()
   584     main_thread = currentThread()
   584 
   585 
   585     def statuschange(status):
   586     def statuschange(status):
   586         wx.CallAfter(taskbar_instance.UpdateIcon,status)
   587         wx.CallAfter(taskbar_instance.UpdateIcon,status)
   587         
   588 
   588     def wx_evaluator(obj, *args, **kwargs):
   589     def wx_evaluator(obj, *args, **kwargs):
   589         tocall,args,kwargs = obj.call
   590         tocall,args,kwargs = obj.call
   590         obj.res = default_evaluator(tocall, *args, **kwargs)
   591         obj.res = default_evaluator(tocall, *args, **kwargs)
   591         wx_eval_lock.release()
   592         wx_eval_lock.release()
   592         
   593 
   593     def evaluator(tocall, *args, **kwargs):
   594     def evaluator(tocall, *args, **kwargs):
   594         global main_thread
   595         global main_thread
   595         if(main_thread == currentThread()):
   596         if(main_thread == currentThread()):
   596             # avoid dead lock if called from the wx mainloop 
   597             # avoid dead lock if called from the wx mainloop
   597             return default_evaluator(tocall, *args, **kwargs)
   598             return default_evaluator(tocall, *args, **kwargs)
   598         else:
   599         else:
   599             o=type('',(object,),dict(call=(tocall, args, kwargs), res=None))
   600             o=type('',(object,),dict(call=(tocall, args, kwargs), res=None))
   600             wx.CallAfter(wx_evaluator,o)
   601             wx.CallAfter(wx_evaluator,o)
   601             wx_eval_lock.acquire()
   602             wx_eval_lock.acquire()
   602             return o.res
   603             return o.res
   603     
   604 
   604     pyroserver = Server(servicename, given_ip, port, WorkingDir, argv, autostart, statuschange, evaluator, website)
   605     pyroserver = Server(servicename, given_ip, port, WorkingDir, argv, autostart, statuschange, evaluator, website)
   605     taskbar_instance = BeremizTaskBarIcon(pyroserver, enablewx)
   606     taskbar_instance = BeremizTaskBarIcon(pyroserver, enablewx)
   606 else:
   607 else:
   607     pyroserver = Server(servicename, given_ip, port, WorkingDir, argv, autostart, website=website)
   608     pyroserver = Server(servicename, given_ip, port, WorkingDir, argv, autostart, website=website)
   608 
   609