beremiz

Fixed encoding of Beremiz_service.py

2011-11-18, Edouard Tisserant
b4eae24d05bf
Parents d410fe057543
Children cd7ccbbbf471
Fixed encoding of Beremiz_service.py
  • +716 -717
    Beremiz_service.py
  • --- a/Beremiz_service.py Fri Nov 18 18:40:19 2011 +0100
    +++ b/Beremiz_service.py Fri Nov 18 23:54:03 2011 +0100
    @@ -1,717 +1,716 @@
    -#!/usr/bin/env python
    -# -*- coding: utf-8 -*-
    -
    -#This file is part of Beremiz, a Integrated Development Environment for
    -#programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
    -#
    -#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
    -#
    -#See COPYING file for copyrights details.
    -#
    -#This library is free software; you can redistribute it and/or
    -#modify it under the terms of the GNU General Public
    -#License as published by the Free Software Foundation; either
    -#version 2.1 of the License, or (at your option) any later version.
    -#
    -#This library is distributed in the hope that it will be useful,
    -#but WITHOUT ANY WARRANTY; without even the implied warranty of
    -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    -#General Public License for more details.
    -#
    -#You should have received a copy of the GNU General Public
    -#License along with this library; if not, write to the Free Software
    -#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    -
    -import os, sys, getopt
    -
    -def usage():
    - print """
    -Usage of Beremiz PLC execution service :\n
    -%s {[-n servicename] [-i ip] [-p port] [-x enabletaskbar] [-a autostart]|-h|--help} working_dir
    - -n - zeroconf service name (default:disabled)
    - -i - ip of interface to bind to (default:localhost)
    - -p - port number default:3000
    - -h - print this help text and quit
    - -a - autostart PLC (0:disable 1:enable) (default:0)
    - -x - enable/disable wxTaskbarIcon (0:disable 1:enable) (default:1)
    - -t - enable/disable Twisted web interface (0:disable 1:enable) (default:1)
    -
    - working_dir - directory where are stored PLC files
    -"""%sys.argv[0]
    -
    -try:
    - opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:t:a:h")
    -except getopt.GetoptError, err:
    - # print help information and exit:
    - print str(err) # will print something like "option -a not recognized"
    - usage()
    - sys.exit(2)
    -
    -# default values
    -ip = ""
    -port = 3000
    -servicename = None
    -autostart = False
    -enablewx = True
    -havewx = False
    -enabletwisted = True
    -havetwisted = False
    -
    -for o, a in opts:
    - if o == "-h":
    - usage()
    - sys.exit()
    - elif o == "-i":
    - if len(a.split(".")) == 4 or a == "localhost":
    - ip = a
    - elif o == "-p":
    - # port: port that the service runs on
    - port = int(a)
    - elif o == "-n":
    - servicename = a
    - elif o == "-x":
    - enablewx = int(a)
    - elif o == "-t":
    - enabletwisted = int(a)
    - elif o == "-a":
    - autostart = int(a)
    - else:
    - usage()
    - sys.exit()
    -
    -if len(argv) > 1:
    - usage()
    - sys.exit()
    -elif len(argv) == 1:
    - WorkingDir = argv[0]
    - os.chdir(WorkingDir)
    -elif len(argv) == 0:
    - WorkingDir = os.getcwd()
    - argv=[WorkingDir]
    -
    -import __builtin__
    -if __name__ == '__main__':
    - __builtin__.__dict__['_'] = lambda x: x
    -
    -if enablewx:
    - try:
    - import wx, re
    - from threading import Thread, currentThread
    - from types import *
    - havewx = True
    - except:
    - print "Wx unavailable !"
    - havewx = False
    -
    - if havewx:
    - app=wx.App(redirect=False)
    -
    - # Import module for internationalization
    - import gettext
    -
    - CWD = os.path.split(os.path.realpath(__file__))[0]
    -
    - # Get folder containing translation files
    - localedir = os.path.join(CWD,"locale")
    - # Get the default language
    - langid = wx.LANGUAGE_DEFAULT
    - # Define translation domain (name of translation files)
    - domain = "Beremiz"
    -
    - # Define locale for wx
    - loc = __builtin__.__dict__.get('loc', None)
    - if loc is None:
    - loc = wx.Locale(langid)
    - __builtin__.__dict__['loc'] = loc
    - # Define location for searching translation files
    - loc.AddCatalogLookupPathPrefix(localedir)
    - # Define locale domain
    - loc.AddCatalog(domain)
    -
    - def unicode_translation(message):
    - return wx.GetTranslation(message).encode("utf-8")
    -
    - if __name__ == '__main__':
    - __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
    -
    - try:
    - from wx.lib.embeddedimage import PyEmbeddedImage
    - except:
    - import cStringIO
    - import base64
    -
    - class PyEmbeddedImage:
    - def __init__(self, image_string):
    - stream = cStringIO.StringIO(base64.b64decode(image_string))
    - self.Image = wx.ImageFromStream(stream)
    - def GetImage(self):
    - return self.Image
    -
    - defaulticon = PyEmbeddedImage(
    - "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")
    -
    - #----------------------------------------------------------------------
    - starticon = PyEmbeddedImage(
    - "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"
    - "Dw/HnwT4P6CDl+TMvD0JAAAAAElFTkSuQmCC")
    -
    - #----------------------------------------------------------------------
    - stopicon = PyEmbeddedImage(
    - "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")
    -
    - class ParamsEntryDialog(wx.TextEntryDialog):
    - if wx.VERSION < (2, 6, 0):
    - def Bind(self, event, function, id = None):
    - if id is not None:
    - event(self, id, function)
    - else:
    - event(self, function)
    -
    -
    - def __init__(self, parent, message, caption = "Please enter text", defaultValue = "",
    - style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
    - wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
    -
    - self.Tests = []
    - if wx.VERSION >= (2, 8, 0):
    - self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId())
    - elif wx.VERSION >= (2, 6, 0):
    - self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId())
    - else:
    - self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
    -
    - def OnOK(self, event):
    - value = self.GetValue()
    - texts = {"value" : value}
    - for function, message in self.Tests:
    - if not function(value):
    - message = wx.MessageDialog(self, message%texts, _("Error"), wx.OK|wx.ICON_ERROR)
    - message.ShowModal()
    - message.Destroy()
    - return
    - self.EndModal(wx.ID_OK)
    - event.Skip()
    -
    - def GetValue(self):
    - return self.GetSizer().GetItem(1).GetWindow().GetValue()
    -
    - def SetTests(self, tests):
    - self.Tests = tests
    -
    - class BeremizTaskBarIcon(wx.TaskBarIcon):
    - TBMENU_START = wx.NewId()
    - TBMENU_STOP = wx.NewId()
    - TBMENU_CHANGE_NAME = wx.NewId()
    - TBMENU_CHANGE_PORT = wx.NewId()
    - TBMENU_CHANGE_INTERFACE = wx.NewId()
    - TBMENU_LIVE_SHELL = wx.NewId()
    - TBMENU_WXINSPECTOR = wx.NewId()
    - TBMENU_CHANGE_WD = wx.NewId()
    - TBMENU_QUIT = wx.NewId()
    -
    - def __init__(self, pyroserver):
    - wx.TaskBarIcon.__init__(self)
    - self.pyroserver = pyroserver
    - # Set the image
    - self.UpdateIcon(None)
    -
    - # bind some events
    - self.Bind(wx.EVT_MENU, self.OnTaskBarStartPLC, id=self.TBMENU_START)
    - self.Bind(wx.EVT_MENU, self.OnTaskBarStopPLC, id=self.TBMENU_STOP)
    - self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME)
    - self.Bind(wx.EVT_MENU, self.OnTaskBarChangeInterface, id=self.TBMENU_CHANGE_INTERFACE)
    - self.Bind(wx.EVT_MENU, self.OnTaskBarLiveShell, id=self.TBMENU_LIVE_SHELL)
    - self.Bind(wx.EVT_MENU, self.OnTaskBarWXInspector, id=self.TBMENU_WXINSPECTOR)
    - self.Bind(wx.EVT_MENU, self.OnTaskBarChangePort, id=self.TBMENU_CHANGE_PORT)
    - self.Bind(wx.EVT_MENU, self.OnTaskBarChangeWorkingDir, id=self.TBMENU_CHANGE_WD)
    - self.Bind(wx.EVT_MENU, self.OnTaskBarQuit, id=self.TBMENU_QUIT)
    -
    - def CreatePopupMenu(self):
    - """
    - This method is called by the base class when it needs to popup
    - the menu for the default EVT_RIGHT_DOWN event. Just create
    - the menu how you want it and return it from this function,
    - the base class takes care of the rest.
    - """
    - menu = wx.Menu()
    - menu.Append(self.TBMENU_START, _("Start PLC"))
    - menu.Append(self.TBMENU_STOP, _("Stop PLC"))
    - menu.Append(self.TBMENU_CHANGE_NAME, _("Change Name"))
    - menu.Append(self.TBMENU_CHANGE_INTERFACE, _("Change IP of interface to bind"))
    - menu.Append(self.TBMENU_LIVE_SHELL, _("Launch a live Python shell"))
    - menu.Append(self.TBMENU_WXINSPECTOR, _("Launch WX GUI inspector"))
    - menu.Append(self.TBMENU_CHANGE_PORT, _("Change Port Number"))
    - menu.AppendSeparator()
    - menu.Append(self.TBMENU_CHANGE_WD, _("Change working directory"))
    - menu.Append(self.TBMENU_QUIT, _("Quit"))
    - return menu
    -
    - def MakeIcon(self, img):
    - """
    - The various platforms have different requirements for the
    - icon size...
    - """
    - if "wxMSW" in wx.PlatformInfo:
    - img = img.Scale(16, 16)
    - elif "wxGTK" in wx.PlatformInfo:
    - img = img.Scale(22, 22)
    - # wxMac can be any size upto 128x128, so leave the source img alone....
    - icon = wx.IconFromBitmap(img.ConvertToBitmap() )
    - return icon
    -
    - def OnTaskBarStartPLC(self, evt):
    - if self.pyroserver.plcobj is not None:
    - self.pyroserver.plcobj.StartPLC()
    - evt.Skip()
    -
    - def OnTaskBarStopPLC(self, evt):
    - if self.pyroserver.plcobj is not None:
    - self.pyroserver.plcobj.StopPLC()
    - evt.Skip()
    -
    - def OnTaskBarChangeInterface(self, evt):
    - dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=self.pyroserver.ip)
    - dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, _("IP is not valid!")),
    - ( lambda ip :len([x for x in ip.split(".") if 0 <= int(x) <= 255]) == 4, _("IP is not valid!"))
    - ])
    - if dlg.ShowModal() == wx.ID_OK:
    - self.pyroserver.ip = dlg.GetValue()
    - self.pyroserver.Stop()
    - evt.Skip()
    -
    - def OnTaskBarChangePort(self, evt):
    - dlg = ParamsEntryDialog(None, _("Enter a port number "), defaultValue=str(self.pyroserver.port))
    - dlg.SetTests([(UnicodeType.isdigit, _("Port number must be an integer!")), (lambda port : 0 <= int(port) <= 65535 , _("Port number must be 0 <= port <= 65535!"))])
    - if dlg.ShowModal() == wx.ID_OK:
    - self.pyroserver.port = int(dlg.GetValue())
    - self.pyroserver.Stop()
    - evt.Skip()
    -
    - def OnTaskBarChangeWorkingDir(self, evt):
    - dlg = wx.DirDialog(None, _("Choose a working directory "), self.pyroserver.workdir, wx.DD_NEW_DIR_BUTTON)
    - if dlg.ShowModal() == wx.ID_OK:
    - self.pyroserver.workdir = dlg.GetPath()
    - self.pyroserver.Stop()
    - evt.Skip()
    -
    - def OnTaskBarChangeName(self, evt):
    - dlg = ParamsEntryDialog(None, _("Enter a name "), defaultValue=self.pyroserver.name)
    - dlg.SetTests([(lambda name : len(name) is not 0 , _("Name must not be null!"))])
    - if dlg.ShowModal() == wx.ID_OK:
    - self.pyroserver.name = dlg.GetValue()
    - self.pyroserver.Restart()
    - evt.Skip()
    -
    - def OnTaskBarLiveShell(self, evt):
    - if self.pyroserver.plcobj is not None and self.pyroserver.plcobj.python_threads_vars is not None:
    - from wx import py
    - #frame = py.shell.ShellFrame(locals=self.pyroserver.plcobj.python_threads_vars)
    - frame = py.crust.CrustFrame(locals=self.pyroserver.plcobj.python_threads_vars)
    - frame.Show()
    - else:
    - wx.MessageBox(_("No running PLC"), _("Error"))
    - evt.Skip()
    -
    - def OnTaskBarWXInspector(self, evt):
    - # Activate the widget inspection tool
    - from wx.lib.inspection import InspectionTool
    - if not InspectionTool().initialized:
    - InspectionTool().Init(locals=self.pyroserver.plcobj.python_threads_vars)
    -
    - # Find a widget to be selected in the tree. Use either the
    - # one under the cursor, if any, or this frame.
    - wnd = wx.FindWindowAtPointer()
    - if not wnd:
    - wnd = wx.GetApp()
    - InspectionTool().Show(wnd, True)
    - evt.Skip()
    -
    - def OnTaskBarQuit(self, evt):
    - self.pyroserver.Quit()
    - self.RemoveIcon()
    - wx.CallAfter(wx.GetApp().Exit)
    - evt.Skip()
    -
    - def UpdateIcon(self, plcstatus):
    - if plcstatus is "Started" :
    - currenticon = self.MakeIcon(starticon.GetImage())
    - elif plcstatus is "Stopped":
    - currenticon = self.MakeIcon(stopicon.GetImage())
    - else:
    - currenticon = self.MakeIcon(defaulticon.GetImage())
    - self.SetIcon(currenticon, "Beremiz Service")
    -
    -from runtime import PLCObject, PLCprint, ServicePublisher
    -import Pyro.core as pyro
    -
    -if not os.path.isdir(WorkingDir):
    - os.mkdir(WorkingDir)
    -
    -def default_evaluator(callable, *args, **kwargs):
    - return callable(*args,**kwargs)
    -
    -class Server():
    - def __init__(self, servicename, ip, port, workdir, argv, autostart=False, statuschange=None, evaluator=default_evaluator, website=None):
    - self.continueloop = True
    - self.daemon = None
    - self.servicename = servicename
    - self.ip = ip
    - self.port = port
    - self.workdir = workdir
    - self.argv = argv
    - self.plcobj = None
    - self.servicepublisher = None
    - self.autostart = autostart
    - self.statuschange = statuschange
    - self.evaluator = evaluator
    - self.website = website
    -
    - def Loop(self):
    - while self.continueloop:
    - self.Start()
    -
    - def Restart(self):
    - self.Stop()
    -
    - def Quit(self):
    - self.continueloop = False
    - self.Stop()
    -
    - def Start(self):
    - pyro.initServer()
    - self.daemon=pyro.Daemon(host=self.ip, port=self.port)
    - self.plcobj = PLCObject(self.workdir, self.daemon, self.argv, self.statuschange, self.evaluator, self.website)
    - uri = self.daemon.connect(self.plcobj,"PLCObject")
    -
    - print "Pyro port :",self.port
    - print "Pyro object's uri :",uri
    - print "Current working directory :",self.workdir
    -
    - # Configure and publish service
    - # Not publish service if localhost in address params
    - if self.servicename is not None and self.ip != "localhost" and self.ip != "127.0.0.1":
    - print "Publishing service on local network"
    - self.servicepublisher = ServicePublisher.ServicePublisher()
    - self.servicepublisher.RegisterService(self.servicename, self.ip, self.port)
    -
    - if self.autostart:
    - self.plcobj.StartPLC()
    -
    - sys.stdout.flush()
    -
    - self.daemon.requestLoop()
    -
    - def Stop(self):
    - self.plcobj.StopPLC()
    - if self.servicepublisher is not None:
    - self.servicepublisher.UnRegisterService()
    - self.servicepublisher = None
    - self.daemon.shutdown(True)
    -
    -if enabletwisted:
    - import warnings
    - with warnings.catch_warnings():
    - warnings.simplefilter("ignore")
    - try:
    - from threading import Thread, currentThread
    - if havewx:
    - from twisted.internet import wxreactor
    - wxreactor.install()
    - from twisted.internet import reactor, task
    - from twisted.python import log, util
    - from nevow import rend, appserver, inevow, tags, loaders, athena
    - from nevow.page import renderer
    -
    - havetwisted = True
    - except:
    - print "Twisted unavailable !"
    - havetwisted = False
    -
    -if havetwisted:
    -
    - xhtml_header = '''<?xml version="1.0" encoding="utf-8"?>
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    -"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    -'''
    -
    - class PLCHMI(athena.LiveElement):
    -
    - initialised = False
    -
    - def HMIinitialised(self, result):
    - self.initialised = True
    -
    - def HMIinitialisation(self):
    - self.HMIinitialised(None)
    -
    - class DefaultPLCStartedHMI(PLCHMI):
    - docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
    - tags.h1["PLC IS NOW STARTED"],
    - ])
    -
    - class PLCStoppedHMI(PLCHMI):
    - docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
    - tags.h1["PLC IS STOPPED"],
    - ])
    -
    - class MainPage(athena.LiveElement):
    - jsClass = u"WebInterface.PLC"
    - docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
    - tags.div(id='content')[
    - tags.div(render = tags.directive('PLCElement')),
    - ]])
    -
    - def __init__(self, *a, **kw):
    - athena.LiveElement.__init__(self, *a, **kw)
    - self.pcl_state = False
    - self.HMI = None
    - self.resetPLCStartedHMI()
    -
    - def setPLCState(self, state):
    - self.pcl_state = state
    - if self.HMI is not None:
    - self.callRemote('updateHMI')
    -
    - def setPLCStartedHMI(self, hmi):
    - self.PLCStartedHMIClass = hmi
    -
    - def resetPLCStartedHMI(self):
    - self.PLCStartedHMIClass = DefaultPLCStartedHMI
    -
    - def getHMI(self):
    - return self.HMI
    -
    - def HMIexec(self, function, *args, **kwargs):
    - if self.HMI is not None:
    - getattr(self.HMI, function, lambda:None)(*args, **kwargs)
    - athena.expose(HMIexec)
    -
    - def resetHMI(self):
    - self.HMI = None
    -
    - def PLCElement(self, ctx, data):
    - return self.getPLCElement()
    - renderer(PLCElement)
    -
    - def getPLCElement(self):
    - self.detachFragmentChildren()
    - if self.pcl_state:
    - f = self.PLCStartedHMIClass()
    - else:
    - f = PLCStoppedHMI()
    - f.setFragmentParent(self)
    - self.HMI = f
    - return f
    - athena.expose(getPLCElement)
    -
    - def detachFragmentChildren(self):
    - for child in self.liveFragmentChildren[:]:
    - child.detach()
    -
    - class WebInterface(athena.LivePage):
    -
    - docFactory = loaders.stan([tags.raw(xhtml_header),
    - tags.html(xmlns="http://www.w3.org/1999/xhtml")[
    - tags.head(render=tags.directive('liveglue')),
    - tags.body[
    - tags.div[
    - tags.div( render = tags.directive( "MainPage" ))
    - ]]]])
    - MainPage = MainPage()
    - PLCHMI = PLCHMI
    -
    - def __init__(self, plcState=False, *a, **kw):
    - super(WebInterface, self).__init__(*a, **kw)
    - self.jsModules.mapping[u'WebInterface'] = util.sibpath(__file__, 'webinterface.js')
    - self.plcState = plcState
    - self.MainPage.setPLCState(plcState)
    -
    - def getHMI(self):
    - return self.MainPage.getHMI()
    -
    - def LoadHMI(self, hmi, jsmodules):
    - for name, path in jsmodules.iteritems():
    - self.jsModules.mapping[name] = os.path.join(WorkingDir, path)
    - self.MainPage.setPLCStartedHMI(hmi)
    -
    - def UnLoadHMI(self):
    - self.MainPage.resetPLCStartedHMI()
    -
    - def PLCStarted(self):
    - self.plcState = True
    - self.MainPage.setPLCState(True)
    -
    - def PLCStopped(self):
    - self.plcState = False
    - self.MainPage.setPLCState(False)
    -
    - def renderHTTP(self, ctx):
    - """
    - Force content type to fit with SVG
    - """
    - req = inevow.IRequest(ctx)
    - req.setHeader('Content-type', 'application/xhtml+xml')
    - return super(WebInterface, self).renderHTTP(ctx)
    -
    - def render_MainPage(self, ctx, data):
    - f = self.MainPage
    - f.setFragmentParent(self)
    - return ctx.tag[f]
    -
    - def child_(self, ctx):
    - self.MainPage.detachFragmentChildren()
    - return WebInterface(plcState=self.plcState)
    -
    - def beforeRender(self, ctx):
    - d = self.notifyOnDisconnect()
    - d.addErrback(self.disconnected)
    -
    - def disconnected(self, reason):
    - self.MainPage.resetHMI()
    - #print reason
    - #print "We will be called back when the client disconnects"
    -
    - if havewx:
    - reactor.registerWxApp(app)
    - website = WebInterface()
    - site = appserver.NevowSite(website)
    -
    - website_port = 8009
    - listening = False
    - while not listening:
    - try:
    - reactor.listenTCP(website_port, site)
    - listening = True
    - except:
    - website_port += 1
    - print "Http interface port :",website_port
    -else:
    - website = None
    -
    -if havewx:
    - from threading import Semaphore
    - wx_eval_lock = Semaphore(0)
    - mythread = currentThread()
    -
    - def statuschange(status):
    - wx.CallAfter(taskbar_instance.UpdateIcon,status)
    -
    - eval_res = None
    - def wx_evaluator(callable, *args, **kwargs):
    - global eval_res
    - try:
    - eval_res=callable(*args,**kwargs)
    - except Exception,e:
    - PLCprint("#EXCEPTION : "+str(e))
    - finally:
    - wx_eval_lock.release()
    -
    - def evaluator(callable, *args, **kwargs):
    - # call directly the callable function if call from the wx mainloop (avoid dead lock)
    - if(mythread == currentThread()):
    - callable(*args,**kwargs)
    - else:
    - wx.CallAfter(wx_evaluator,callable,*args,**kwargs)
    - wx_eval_lock.acquire()
    - return eval_res
    -
    - pyroserver = Server(servicename, ip, port, WorkingDir, argv, autostart, statuschange, evaluator, website)
    - taskbar_instance = BeremizTaskBarIcon(pyroserver)
    -else:
    - pyroserver = Server(servicename, ip, port, WorkingDir, argv, autostart, website=website)
    -
    -if havetwisted or havewx:
    - pyro_thread=Thread(target=pyroserver.Loop)
    - pyro_thread.start()
    -
    - if havetwisted:
    - reactor.run()
    - elif havewx:
    - app.MainLoop()
    -else:
    - try :
    - pyroserver.Loop()
    - except KeyboardInterrupt,e:
    - pass
    -pyroserver.Quit()
    -sys.exit(0)
    -
    +#!/usr/bin/env python
    +# -*- coding: utf-8 -*-
    +
    +#This file is part of Beremiz, a Integrated Development Environment for
    +#programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
    +#
    +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
    +#
    +#See COPYING file for copyrights details.
    +#
    +#This library is free software; you can redistribute it and/or
    +#modify it under the terms of the GNU General Public
    +#License as published by the Free Software Foundation; either
    +#version 2.1 of the License, or (at your option) any later version.
    +#
    +#This library is distributed in the hope that it will be useful,
    +#but WITHOUT ANY WARRANTY; without even the implied warranty of
    +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    +#General Public License for more details.
    +#
    +#You should have received a copy of the GNU General Public
    +#License along with this library; if not, write to the Free Software
    +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    +
    +import os, sys, getopt
    +
    +def usage():
    + print """
    +Usage of Beremiz PLC execution service :\n
    +%s {[-n servicename] [-i ip] [-p port] [-x enabletaskbar] [-a autostart]|-h|--help} working_dir
    + -n - zeroconf service name (default:disabled)
    + -i - ip of interface to bind to (default:localhost)
    + -p - port number default:3000
    + -h - print this help text and quit
    + -a - autostart PLC (0:disable 1:enable) (default:0)
    + -x - enable/disable wxTaskbarIcon (0:disable 1:enable) (default:1)
    + -t - enable/disable Twisted web interface (0:disable 1:enable) (default:1)
    +
    + working_dir - directory where are stored PLC files
    +"""%sys.argv[0]
    +
    +try:
    + opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:t:a:h")
    +except getopt.GetoptError, err:
    + # print help information and exit:
    + print str(err) # will print something like "option -a not recognized"
    + usage()
    + sys.exit(2)
    +
    +# default values
    +ip = ""
    +port = 3000
    +servicename = None
    +autostart = False
    +enablewx = True
    +havewx = False
    +enabletwisted = True
    +havetwisted = False
    +
    +for o, a in opts:
    + if o == "-h":
    + usage()
    + sys.exit()
    + elif o == "-i":
    + if len(a.split(".")) == 4 or a == "localhost":
    + ip = a
    + elif o == "-p":
    + # port: port that the service runs on
    + port = int(a)
    + elif o == "-n":
    + servicename = a
    + elif o == "-x":
    + enablewx = int(a)
    + elif o == "-t":
    + enabletwisted = int(a)
    + elif o == "-a":
    + autostart = int(a)
    + else:
    + usage()
    + sys.exit()
    +
    +if len(argv) > 1:
    + usage()
    + sys.exit()
    +elif len(argv) == 1:
    + WorkingDir = argv[0]
    + os.chdir(WorkingDir)
    +elif len(argv) == 0:
    + WorkingDir = os.getcwd()
    + argv=[WorkingDir]
    +
    +import __builtin__
    +if __name__ == '__main__':
    + __builtin__.__dict__['_'] = lambda x: x
    +
    +if enablewx:
    + try:
    + import wx, re
    + from threading import Thread, currentThread
    + from types import *
    + havewx = True
    + except:
    + print "Wx unavailable !"
    + havewx = False
    +
    + if havewx:
    + app=wx.App(redirect=False)
    +
    + # Import module for internationalization
    + import gettext
    +
    + CWD = os.path.split(os.path.realpath(__file__))[0]
    +
    + # Get folder containing translation files
    + localedir = os.path.join(CWD,"locale")
    + # Get the default language
    + langid = wx.LANGUAGE_DEFAULT
    + # Define translation domain (name of translation files)
    + domain = "Beremiz"
    +
    + # Define locale for wx
    + loc = __builtin__.__dict__.get('loc', None)
    + if loc is None:
    + loc = wx.Locale(langid)
    + __builtin__.__dict__['loc'] = loc
    + # Define location for searching translation files
    + loc.AddCatalogLookupPathPrefix(localedir)
    + # Define locale domain
    + loc.AddCatalog(domain)
    +
    + def unicode_translation(message):
    + return wx.GetTranslation(message).encode("utf-8")
    +
    + if __name__ == '__main__':
    + __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
    +
    + try:
    + from wx.lib.embeddedimage import PyEmbeddedImage
    + except:
    + import cStringIO
    + import base64
    +
    + class PyEmbeddedImage:
    + def __init__(self, image_string):
    + stream = cStringIO.StringIO(base64.b64decode(image_string))
    + self.Image = wx.ImageFromStream(stream)
    + def GetImage(self):
    + return self.Image
    +
    + defaulticon = PyEmbeddedImage(
    + "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")
    +
    + #----------------------------------------------------------------------
    + starticon = PyEmbeddedImage(
    + "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"
    + "Dw/HnwT4P6CDl+TMvD0JAAAAAElFTkSuQmCC")
    +
    + #----------------------------------------------------------------------
    + stopicon = PyEmbeddedImage(
    + "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")
    +
    + class ParamsEntryDialog(wx.TextEntryDialog):
    + if wx.VERSION < (2, 6, 0):
    + def Bind(self, event, function, id = None):
    + if id is not None:
    + event(self, id, function)
    + else:
    + event(self, function)
    +
    +
    + def __init__(self, parent, message, caption = "Please enter text", defaultValue = "",
    + style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
    + wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
    +
    + self.Tests = []
    + if wx.VERSION >= (2, 8, 0):
    + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId())
    + elif wx.VERSION >= (2, 6, 0):
    + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId())
    + else:
    + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
    +
    + def OnOK(self, event):
    + value = self.GetValue()
    + texts = {"value" : value}
    + for function, message in self.Tests:
    + if not function(value):
    + message = wx.MessageDialog(self, message%texts, _("Error"), wx.OK|wx.ICON_ERROR)
    + message.ShowModal()
    + message.Destroy()
    + return
    + self.EndModal(wx.ID_OK)
    + event.Skip()
    +
    + def GetValue(self):
    + return self.GetSizer().GetItem(1).GetWindow().GetValue()
    +
    + def SetTests(self, tests):
    + self.Tests = tests
    +
    + class BeremizTaskBarIcon(wx.TaskBarIcon):
    + TBMENU_START = wx.NewId()
    + TBMENU_STOP = wx.NewId()
    + TBMENU_CHANGE_NAME = wx.NewId()
    + TBMENU_CHANGE_PORT = wx.NewId()
    + TBMENU_CHANGE_INTERFACE = wx.NewId()
    + TBMENU_LIVE_SHELL = wx.NewId()
    + TBMENU_WXINSPECTOR = wx.NewId()
    + TBMENU_CHANGE_WD = wx.NewId()
    + TBMENU_QUIT = wx.NewId()
    +
    + def __init__(self, pyroserver):
    + wx.TaskBarIcon.__init__(self)
    + self.pyroserver = pyroserver
    + # Set the image
    + self.UpdateIcon(None)
    +
    + # bind some events
    + self.Bind(wx.EVT_MENU, self.OnTaskBarStartPLC, id=self.TBMENU_START)
    + self.Bind(wx.EVT_MENU, self.OnTaskBarStopPLC, id=self.TBMENU_STOP)
    + self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME)
    + self.Bind(wx.EVT_MENU, self.OnTaskBarChangeInterface, id=self.TBMENU_CHANGE_INTERFACE)
    + self.Bind(wx.EVT_MENU, self.OnTaskBarLiveShell, id=self.TBMENU_LIVE_SHELL)
    + self.Bind(wx.EVT_MENU, self.OnTaskBarWXInspector, id=self.TBMENU_WXINSPECTOR)
    + self.Bind(wx.EVT_MENU, self.OnTaskBarChangePort, id=self.TBMENU_CHANGE_PORT)
    + self.Bind(wx.EVT_MENU, self.OnTaskBarChangeWorkingDir, id=self.TBMENU_CHANGE_WD)
    + self.Bind(wx.EVT_MENU, self.OnTaskBarQuit, id=self.TBMENU_QUIT)
    +
    + def CreatePopupMenu(self):
    + """
    + This method is called by the base class when it needs to popup
    + the menu for the default EVT_RIGHT_DOWN event. Just create
    + the menu how you want it and return it from this function,
    + the base class takes care of the rest.
    + """
    + menu = wx.Menu()
    + menu.Append(self.TBMENU_START, _("Start PLC"))
    + menu.Append(self.TBMENU_STOP, _("Stop PLC"))
    + menu.Append(self.TBMENU_CHANGE_NAME, _("Change Name"))
    + menu.Append(self.TBMENU_CHANGE_INTERFACE, _("Change IP of interface to bind"))
    + menu.Append(self.TBMENU_LIVE_SHELL, _("Launch a live Python shell"))
    + menu.Append(self.TBMENU_WXINSPECTOR, _("Launch WX GUI inspector"))
    + menu.Append(self.TBMENU_CHANGE_PORT, _("Change Port Number"))
    + menu.AppendSeparator()
    + menu.Append(self.TBMENU_CHANGE_WD, _("Change working directory"))
    + menu.Append(self.TBMENU_QUIT, _("Quit"))
    + return menu
    +
    + def MakeIcon(self, img):
    + """
    + The various platforms have different requirements for the
    + icon size...
    + """
    + if "wxMSW" in wx.PlatformInfo:
    + img = img.Scale(16, 16)
    + elif "wxGTK" in wx.PlatformInfo:
    + img = img.Scale(22, 22)
    + # wxMac can be any size upto 128x128, so leave the source img alone....
    + icon = wx.IconFromBitmap(img.ConvertToBitmap() )
    + return icon
    +
    + def OnTaskBarStartPLC(self, evt):
    + if self.pyroserver.plcobj is not None:
    + self.pyroserver.plcobj.StartPLC()
    + evt.Skip()
    +
    + def OnTaskBarStopPLC(self, evt):
    + if self.pyroserver.plcobj is not None:
    + self.pyroserver.plcobj.StopPLC()
    + evt.Skip()
    +
    + def OnTaskBarChangeInterface(self, evt):
    + dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=self.pyroserver.ip)
    + dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, _("IP is not valid!")),
    + ( lambda ip :len([x for x in ip.split(".") if 0 <= int(x) <= 255]) == 4, _("IP is not valid!"))
    + ])
    + if dlg.ShowModal() == wx.ID_OK:
    + self.pyroserver.ip = dlg.GetValue()
    + self.pyroserver.Stop()
    + evt.Skip()
    +
    + def OnTaskBarChangePort(self, evt):
    + dlg = ParamsEntryDialog(None, _("Enter a port number "), defaultValue=str(self.pyroserver.port))
    + dlg.SetTests([(UnicodeType.isdigit, _("Port number must be an integer!")), (lambda port : 0 <= int(port) <= 65535 , _("Port number must be 0 <= port <= 65535!"))])
    + if dlg.ShowModal() == wx.ID_OK:
    + self.pyroserver.port = int(dlg.GetValue())
    + self.pyroserver.Stop()
    + evt.Skip()
    +
    + def OnTaskBarChangeWorkingDir(self, evt):
    + dlg = wx.DirDialog(None, _("Choose a working directory "), self.pyroserver.workdir, wx.DD_NEW_DIR_BUTTON)
    + if dlg.ShowModal() == wx.ID_OK:
    + self.pyroserver.workdir = dlg.GetPath()
    + self.pyroserver.Stop()
    + evt.Skip()
    +
    + def OnTaskBarChangeName(self, evt):
    + dlg = ParamsEntryDialog(None, _("Enter a name "), defaultValue=self.pyroserver.name)
    + dlg.SetTests([(lambda name : len(name) is not 0 , _("Name must not be null!"))])
    + if dlg.ShowModal() == wx.ID_OK:
    + self.pyroserver.name = dlg.GetValue()
    + self.pyroserver.Restart()
    + evt.Skip()
    +
    + def OnTaskBarLiveShell(self, evt):
    + if self.pyroserver.plcobj is not None and self.pyroserver.plcobj.python_threads_vars is not None:
    + from wx import py
    + #frame = py.shell.ShellFrame(locals=self.pyroserver.plcobj.python_threads_vars)
    + frame = py.crust.CrustFrame(locals=self.pyroserver.plcobj.python_threads_vars)
    + frame.Show()
    + else:
    + wx.MessageBox(_("No running PLC"), _("Error"))
    + evt.Skip()
    +
    + def OnTaskBarWXInspector(self, evt):
    + # Activate the widget inspection tool
    + from wx.lib.inspection import InspectionTool
    + if not InspectionTool().initialized:
    + InspectionTool().Init(locals=self.pyroserver.plcobj.python_threads_vars)
    +
    + # Find a widget to be selected in the tree. Use either the
    + # one under the cursor, if any, or this frame.
    + wnd = wx.FindWindowAtPointer()
    + if not wnd:
    + wnd = wx.GetApp()
    + InspectionTool().Show(wnd, True)
    + evt.Skip()
    +
    + def OnTaskBarQuit(self, evt):
    + self.pyroserver.Quit()
    + self.RemoveIcon()
    + wx.CallAfter(wx.GetApp().Exit)
    + evt.Skip()
    +
    + def UpdateIcon(self, plcstatus):
    + if plcstatus is "Started" :
    + currenticon = self.MakeIcon(starticon.GetImage())
    + elif plcstatus is "Stopped":
    + currenticon = self.MakeIcon(stopicon.GetImage())
    + else:
    + currenticon = self.MakeIcon(defaulticon.GetImage())
    + self.SetIcon(currenticon, "Beremiz Service")
    +
    +from runtime import PLCObject, PLCprint, ServicePublisher
    +import Pyro.core as pyro
    +
    +if not os.path.isdir(WorkingDir):
    + os.mkdir(WorkingDir)
    +
    +def default_evaluator(callable, *args, **kwargs):
    + return callable(*args,**kwargs)
    +
    +class Server():
    + def __init__(self, servicename, ip, port, workdir, argv, autostart=False, statuschange=None, evaluator=default_evaluator, website=None):
    + self.continueloop = True
    + self.daemon = None
    + self.servicename = servicename
    + self.ip = ip
    + self.port = port
    + self.workdir = workdir
    + self.argv = argv
    + self.plcobj = None
    + self.servicepublisher = None
    + self.autostart = autostart
    + self.statuschange = statuschange
    + self.evaluator = evaluator
    + self.website = website
    +
    + def Loop(self):
    + while self.continueloop:
    + self.Start()
    +
    + def Restart(self):
    + self.Stop()
    +
    + def Quit(self):
    + self.continueloop = False
    + self.Stop()
    +
    + def Start(self):
    + pyro.initServer()
    + self.daemon=pyro.Daemon(host=self.ip, port=self.port)
    + self.plcobj = PLCObject(self.workdir, self.daemon, self.argv, self.statuschange, self.evaluator, self.website)
    + uri = self.daemon.connect(self.plcobj,"PLCObject")
    +
    + print "Pyro port :",self.port
    + print "Pyro object's uri :",uri
    + print "Current working directory :",self.workdir
    +
    + # Configure and publish service
    + # Not publish service if localhost in address params
    + if self.servicename is not None and self.ip != "localhost" and self.ip != "127.0.0.1":
    + print "Publishing service on local network"
    + self.servicepublisher = ServicePublisher.ServicePublisher()
    + self.servicepublisher.RegisterService(self.servicename, self.ip, self.port)
    +
    + if self.autostart:
    + self.plcobj.StartPLC()
    +
    + sys.stdout.flush()
    +
    + self.daemon.requestLoop()
    +
    + def Stop(self):
    + self.plcobj.StopPLC()
    + if self.servicepublisher is not None:
    + self.servicepublisher.UnRegisterService()
    + self.servicepublisher = None
    + self.daemon.shutdown(True)
    +
    +if enabletwisted:
    + import warnings
    + with warnings.catch_warnings():
    + warnings.simplefilter("ignore")
    + try:
    + from threading import Thread, currentThread
    + if havewx:
    + from twisted.internet import wxreactor
    + wxreactor.install()
    + from twisted.internet import reactor, task
    + from twisted.python import log, util
    + from nevow import rend, appserver, inevow, tags, loaders, athena
    + from nevow.page import renderer
    +
    + havetwisted = True
    + except:
    + print "Twisted unavailable !"
    + havetwisted = False
    +
    +if havetwisted:
    +
    + xhtml_header = '''<?xml version="1.0" encoding="utf-8"?>
    +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    +"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    +'''
    +
    + class PLCHMI(athena.LiveElement):
    +
    + initialised = False
    +
    + def HMIinitialised(self, result):
    + self.initialised = True
    +
    + def HMIinitialisation(self):
    + self.HMIinitialised(None)
    +
    + class DefaultPLCStartedHMI(PLCHMI):
    + docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
    + tags.h1["PLC IS NOW STARTED"],
    + ])
    +
    + class PLCStoppedHMI(PLCHMI):
    + docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
    + tags.h1["PLC IS STOPPED"],
    + ])
    +
    + class MainPage(athena.LiveElement):
    + jsClass = u"WebInterface.PLC"
    + docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
    + tags.div(id='content')[
    + tags.div(render = tags.directive('PLCElement')),
    + ]])
    +
    + def __init__(self, *a, **kw):
    + athena.LiveElement.__init__(self, *a, **kw)
    + self.pcl_state = False
    + self.HMI = None
    + self.resetPLCStartedHMI()
    +
    + def setPLCState(self, state):
    + self.pcl_state = state
    + if self.HMI is not None:
    + self.callRemote('updateHMI')
    +
    + def setPLCStartedHMI(self, hmi):
    + self.PLCStartedHMIClass = hmi
    +
    + def resetPLCStartedHMI(self):
    + self.PLCStartedHMIClass = DefaultPLCStartedHMI
    +
    + def getHMI(self):
    + return self.HMI
    +
    + def HMIexec(self, function, *args, **kwargs):
    + if self.HMI is not None:
    + getattr(self.HMI, function, lambda:None)(*args, **kwargs)
    + athena.expose(HMIexec)
    +
    + def resetHMI(self):
    + self.HMI = None
    +
    + def PLCElement(self, ctx, data):
    + return self.getPLCElement()
    + renderer(PLCElement)
    +
    + def getPLCElement(self):
    + self.detachFragmentChildren()
    + if self.pcl_state:
    + f = self.PLCStartedHMIClass()
    + else:
    + f = PLCStoppedHMI()
    + f.setFragmentParent(self)
    + self.HMI = f
    + return f
    + athena.expose(getPLCElement)
    +
    + def detachFragmentChildren(self):
    + for child in self.liveFragmentChildren[:]:
    + child.detach()
    +
    + class WebInterface(athena.LivePage):
    +
    + docFactory = loaders.stan([tags.raw(xhtml_header),
    + tags.html(xmlns="http://www.w3.org/1999/xhtml")[
    + tags.head(render=tags.directive('liveglue')),
    + tags.body[
    + tags.div[
    + tags.div( render = tags.directive( "MainPage" ))
    + ]]]])
    + MainPage = MainPage()
    + PLCHMI = PLCHMI
    +
    + def __init__(self, plcState=False, *a, **kw):
    + super(WebInterface, self).__init__(*a, **kw)
    + self.jsModules.mapping[u'WebInterface'] = util.sibpath(__file__, 'webinterface.js')
    + self.plcState = plcState
    + self.MainPage.setPLCState(plcState)
    +
    + def getHMI(self):
    + return self.MainPage.getHMI()
    +
    + def LoadHMI(self, hmi, jsmodules):
    + for name, path in jsmodules.iteritems():
    + self.jsModules.mapping[name] = os.path.join(WorkingDir, path)
    + self.MainPage.setPLCStartedHMI(hmi)
    +
    + def UnLoadHMI(self):
    + self.MainPage.resetPLCStartedHMI()
    +
    + def PLCStarted(self):
    + self.plcState = True
    + self.MainPage.setPLCState(True)
    +
    + def PLCStopped(self):
    + self.plcState = False
    + self.MainPage.setPLCState(False)
    +
    + def renderHTTP(self, ctx):
    + """
    + Force content type to fit with SVG
    + """
    + req = inevow.IRequest(ctx)
    + req.setHeader('Content-type', 'application/xhtml+xml')
    + return super(WebInterface, self).renderHTTP(ctx)
    +
    + def render_MainPage(self, ctx, data):
    + f = self.MainPage
    + f.setFragmentParent(self)
    + return ctx.tag[f]
    +
    + def child_(self, ctx):
    + self.MainPage.detachFragmentChildren()
    + return WebInterface(plcState=self.plcState)
    +
    + def beforeRender(self, ctx):
    + d = self.notifyOnDisconnect()
    + d.addErrback(self.disconnected)
    +
    + def disconnected(self, reason):
    + self.MainPage.resetHMI()
    + #print reason
    + #print "We will be called back when the client disconnects"
    +
    + if havewx:
    + reactor.registerWxApp(app)
    + website = WebInterface()
    + site = appserver.NevowSite(website)
    +
    + website_port = 8009
    + listening = False
    + while not listening:
    + try:
    + reactor.listenTCP(website_port, site)
    + listening = True
    + except:
    + website_port += 1
    + print "Http interface port :",website_port
    +else:
    + website = None
    +
    +if havewx:
    + from threading import Semaphore
    + wx_eval_lock = Semaphore(0)
    + mythread = currentThread()
    +
    + def statuschange(status):
    + wx.CallAfter(taskbar_instance.UpdateIcon,status)
    +
    + eval_res = None
    + def wx_evaluator(callable, *args, **kwargs):
    + global eval_res
    + try:
    + eval_res=callable(*args,**kwargs)
    + except Exception,e:
    + PLCprint("#EXCEPTION : "+str(e))
    + finally:
    + wx_eval_lock.release()
    +
    + def evaluator(callable, *args, **kwargs):
    + # call directly the callable function if call from the wx mainloop (avoid dead lock)
    + if(mythread == currentThread()):
    + callable(*args,**kwargs)
    + else:
    + wx.CallAfter(wx_evaluator,callable,*args,**kwargs)
    + wx_eval_lock.acquire()
    + return eval_res
    +
    + pyroserver = Server(servicename, ip, port, WorkingDir, argv, autostart, statuschange, evaluator, website)
    + taskbar_instance = BeremizTaskBarIcon(pyroserver)
    +else:
    + pyroserver = Server(servicename, ip, port, WorkingDir, argv, autostart, website=website)
    +
    +if havetwisted or havewx:
    + pyro_thread=Thread(target=pyroserver.Loop)
    + pyro_thread.start()
    +
    + if havetwisted:
    + reactor.run()
    + elif havewx:
    + app.MainLoop()
    +else:
    + try :
    + pyroserver.Loop()
    + except KeyboardInterrupt,e:
    + pass
    +pyroserver.Quit()
    +sys.exit(0)