Beispiel #1
0
    def disconnect_client(self, timeout=DEFAULT_TIMEOUT):
        future = asyncio.Future(loop=self.loop)
        self.subscriptions = []
        self.router = PathRouter()

        def off():
            if hasattr(self, '_on_off_events'):
                del self._on_off_events

        if (_.get(self, 'client._state') != mqtt_cs_connected):
            off()
            if (hasattr(self, 'client')):
                del self.client
            future.set_result('done')
            return future
        else:

            def on_disconnect(*args, **kwargs):
                if future.done():
                    return
                off()
                if (hasattr(self, 'client')):
                    del self.client

                future.set_result('done')

            if hasattr(self, 'client'):
                self.client.on_disconnect = self.safe(on_disconnect)
                self.client.disconnect()
                set_timeout(self.safe(on_disconnect), timeout)
            else:
                if (future.done() == False):
                    future.set_result('done')

        return future
Beispiel #2
0
 def router_option_extender(options):
     from wheezy.routing import PathRouter
     from wheezy.web.middleware.routing import PathRoutingMiddleware
     path_router = PathRouter()
     path_router.add_routes(urls() if callable(urls) else urls)
     options['path_router'] = path_router
     options['path_for'] = path_router.path_for
     return PathRoutingMiddleware(path_router)
Beispiel #3
0
 def __init__(self, host="localhost", port=1883, base="microdrop"):
     super().__init__()
     self.router = PathRouter()
     self.host = host
     self.port = port
     self.base = base
     self.subscriptions = []
     self.client_id = self.ClientID()
     self.client = self.Client()
     self.connected = False
Beispiel #4
0
    def __init__(self,
                 app_name,
                 host="localhost",
                 port=None,
                 name=None,
                 version='0.0.0',
                 loop=None):
        if (app_name is None):
            raise ("app_name is undefined")

        if (port is None):
            port = 1884

        if (name is None):
            name = get_class_name(self)

        self.router = PathRouter()
        client_id = generate_client_id(name, app_name)
        self.__listen = _.noop

        self.app_name = app_name
        self.client_id = client_id
        self.name = name
        self.schema = {}
        self.subscriptions = []
        self.host = host
        self.port = port
        self.version = version
        self.last_message = None
        self.loop = None
        self.safe = None
        self.client = None

        if (loop == None):
            # Create thread to run event loop
            def start_loop(x):
                x.loop = asyncio.new_event_loop()
                x.loop.call_soon_threadsafe(x.ready_event.set)
                x.loop.run_forever()

            # Initialize loop and pass reference to main thread
            class X(object):
                pass

            X.ready_event = threading.Event()
            t = Thread(target=start_loop, args=(X, ))
            t.start()
            X.ready_event.wait()
            self.loop = X.loop
        else:
            self.loop = loop
        self.safe = safe(self.loop)

        # Start client
        self.wait_for(self.connect_client(client_id, host, port))
Beispiel #5
0
 def __init__(self,
              host='localhost',
              port=1883,
              keepalive=60,
              base="microdrop"):
     self._host = host
     self._port = port
     self._keepalive = keepalive
     self.mqtt_client = mqtt.Client(client_id=self.client_id)
     self.mqtt_client.on_connect = self.on_connect
     self.mqtt_client.on_disconnect = self.on_disconnect
     self.mqtt_client.on_message = self.on_message
     self.should_exit = False
     self.router = PathRouter()
     self.subscriptions = []
     self.base = base
Beispiel #6
0
 def __init__(self, **kwargs):
     super().__init__()
     self.library = PicroscopyLibrary(**kwargs)
     self.helpers = WebHelpers(self.library)
     self.clients = kwargs.get('clients', IPv4Network('0.0.0.0/0'))
     logging.info('Clients must be on network %s', self.clients)
     self.static_dir = os.path.abspath(
         os.path.normpath(
             kwargs.get('static_dir', os.path.join(HERE, 'static'))))
     logging.info('Static files: %s', self.static_dir)
     self.templates_dir = os.path.abspath(
         os.path.normpath(
             kwargs.get('templates_dir', os.path.join(HERE, 'templates'))))
     logging.info('Chameleon templates: %s', self.templates_dir)
     self.templates = PageTemplateLoader(self.templates_dir,
                                         default_extension='.pt')
     self.layout = self.templates['layout']
     # No need to make flashes a per-session thing - it's a single user app!
     self.flashes = []
     self.router = PathRouter()
     # XXX Add handler for exiting system
     # XXX Make exit code conditional? (upgrade/reboot/shutdown/etc.)
     self.router.add_routes([
         url('/', self.do_template, kwargs={'page': 'library'},
             name='home'),
         url('/{page}.html', self.do_template, name='template'),
         url('/view/{image}.html',
             self.do_template,
             kwargs={'page': 'image'},
             name='view'),
         url('/static/{path:any}', self.do_static, name='static'),
         url('/images/{image}', self.do_image, name='image'),
         url('/thumbs/{image}', self.do_thumb, name='thumb'),
         url('/delete/{image}', self.do_delete, name='delete'),
         url('/config', self.do_config, name='config'),
         url('/reset', self.do_reset, name='reset'),
         url('/capture', self.do_capture, name='capture'),
         url('/download', self.do_download, name='download'),
         url('/send', self.do_send, name='send'),
         url('/logout', self.do_logout, name='logout'),
     ])
 def __init__(self, **kwargs):
   self.form = WebControlFormHelper(**kwargs)
   self.logger = kwargs.get('logger',)
   self.robot = kwargs.get('robot')
   #initialize speed in form with robot speed
   self.form.speed = self.robot.setDriveSpeed
   #create routes to web pages
   self.router = PathRouter()
   self.router.add_routes([
     url('/', self.do_main_page, name='home'),
     url('/doRobotControl', self.do_process_form, name='execute'),
     url('/showRobotControlForm', self.do_display_form, name='view')
     ])
Beispiel #8
0
 def __init__(self, **kwargs):
     super(MancifyWsgiApp, self).__init__()
     self.sms = MancifySMSService(kwargs['clockwork_api_key'])
     self.exec_timeout = kwargs.get('exec_timeout', 10)
     self.connect_timeout = kwargs.get('connect_timeout', 30)
     self.session_timeout = kwargs.get('session_timeout', 300)
     self.output_limit = kwargs.get('output_limit', 1024)
     self.router = PathRouter()
     self.router.add_routes([
         url('/',          self.do_index),
         url('/ssh',       self.do_ssh),
         url('/translate', self.do_translate),
         ])
     self.lock = threading.Lock()
     self.sessions = {}
     self.messages = set()
     self.terminate = threading.Event()
     self.reap_thread = threading.Thread(target=self.reap_sessions)
     self.reap_thread.daemon = True
     self.reap_thread.start()
Beispiel #9
0
 def __init__(self, **kwargs):
     super().__init__()
     self.library = PicroscopyLibrary(**kwargs)
     self.helpers = WebHelpers(self.library)
     self.clients = kwargs.get('clients', IPv4Network('0.0.0.0/0'))
     logging.info('Clients must be on network %s', self.clients)
     self.static_dir = os.path.abspath(os.path.normpath(kwargs.get(
         'static_dir', os.path.join(HERE, 'static')
         )))
     logging.info('Static files: %s', self.static_dir)
     self.templates_dir = os.path.abspath(os.path.normpath(kwargs.get(
         'templates_dir', os.path.join(HERE, 'templates')
         )))
     logging.info('Chameleon templates: %s', self.templates_dir)
     self.templates = PageTemplateLoader(
         self.templates_dir, default_extension='.pt')
     self.layout = self.templates['layout']
     # No need to make flashes a per-session thing - it's a single user app!
     self.flashes = []
     self.router = PathRouter()
     # XXX Add handler for exiting system
     # XXX Make exit code conditional? (upgrade/reboot/shutdown/etc.)
     self.router.add_routes([
         url('/',                   self.do_template, kwargs={'page': 'library'}, name='home'),
         url('/{page}.html',        self.do_template, name='template'),
         url('/view/{image}.html',  self.do_template, kwargs={'page': 'image'}, name='view'),
         url('/static/{path:any}',  self.do_static,   name='static'),
         url('/images/{image}',     self.do_image,    name='image'),
         url('/thumbs/{image}',     self.do_thumb,    name='thumb'),
         url('/delete/{image}',     self.do_delete,   name='delete'),
         url('/config',             self.do_config,   name='config'),
         url('/reset',              self.do_reset,    name='reset'),
         url('/capture',            self.do_capture,  name='capture'),
         url('/download',           self.do_download, name='download'),
         url('/send',               self.do_send,     name='send'),
         url('/logout',             self.do_logout,   name='logout'),
         ])
Beispiel #10
0
class MancifyWsgiApp(object):
    def __init__(self, **kwargs):
        super(MancifyWsgiApp, self).__init__()
        self.sms = MancifySMSService(kwargs['clockwork_api_key'])
        self.exec_timeout = kwargs.get('exec_timeout', 10)
        self.connect_timeout = kwargs.get('connect_timeout', 30)
        self.session_timeout = kwargs.get('session_timeout', 300)
        self.output_limit = kwargs.get('output_limit', 1024)
        self.router = PathRouter()
        self.router.add_routes([
            url('/',          self.do_index),
            url('/ssh',       self.do_ssh),
            url('/translate', self.do_translate),
            ])
        self.lock = threading.Lock()
        self.sessions = {}
        self.messages = set()
        self.terminate = threading.Event()
        self.reap_thread = threading.Thread(target=self.reap_sessions)
        self.reap_thread.daemon = True
        self.reap_thread.start()

    def close(self):
        self.terminate.set()
        self.reap_thread.join(5)

    def reap_sessions(self):
        while True:
            reap_list = []
            now = time.time()
            with self.lock:
                for recipient, session in self.sessions.iteritems():
                    if not session.timestamp:
                        reap_list.append((recipient, session))
                    if (now - session.timestamp) > self.session_timeout:
                        reap_list.append((recipient, session))
                for recipient, session in reap_list:
                    try:
                        session.close(quiet=True)
                    finally:
                        del self.sessions[recipient]
                        session = None
            if self.terminate.wait(10):
                break

    def __call__(self, environ, start_response):
        req = Request(environ)
        try:
            handler, kwargs = self.router.match(req.path_info)
            if handler:
                # XXX Workaround wheezy bug
                if 'route_name' in kwargs:
                    del kwargs['route_name']
                resp = handler(req, **kwargs)
            else:
                self.not_found(req)
        except exc.HTTPException as e:
            # The exception is the response
            resp = e
        return resp(environ, start_response)

    def not_found(self, req):
        raise exc.HTTPNotFound(
            "The resource at %s could not be found" % req.path_info)

    def do_index(self, req):
        resp = Response()
        resp.content_type = b'text/html'
        resp.content_encoding = b'utf-8'
        resp.text = """\
<html>
<head><title>Mancify</title></head>
<body>
<h1>Mancify</h1>
<p>Probably the silliest webapp in the world...</p>
</body>
</html>
"""
        return resp

    def do_translate(self, req):
        # Check the request has the required parameters
        if not 'msg_id' in req.params:
            raise exc.HTTPBadRequest('Missing msg_id parameter')
        if not 'from' in req.params:
            raise exc.HTTPBadRequest('Missing from parameter')
        if not 'content' in req.params:
            raise exc.HTTPBadRequest('Missing content parameter')
        msg_id = req.params['msg_id']
        recipient = req.params['from']
        sender = req.params['to']
        content = req.params['content']
        # If we've seen the message before it's a duplicate. Return 200 OK so
        # the server doesn't keep retrying but otherwise ignore it
        if msg_id in self.messages:
            raise exc.HTTPOk('Message already processed')
        self.messages.add(msg_id)
        self.sms.send(sender, recipient, translator.translate(content, manc))
        raise exc.HTTPOk('Message processed')

    def do_ssh(self, req):
        # Check the request has the required parameters
        if not 'msg_id' in req.params:
            raise exc.HTTPBadRequest('Missing msg_id parameter')
        if not 'from' in req.params:
            raise exc.HTTPBadRequest('Missing from parameter')
        if not 'content' in req.params:
            raise exc.HTTPBadRequest('Missing content parameter')
        msg_id = req.params['msg_id']
        recipient = req.params['from']
        sender = req.params['to']
        content = req.params['content']
        # If we've seen the message before it's a duplicate. Return 200 OK so
        # the server doesn't keep retrying but otherwise ignore it
        if msg_id in self.messages:
            raise exc.HTTPOk('Message already processed')
        self.messages.add(msg_id)
        try:
            with self.lock:
                try:
                    session = self.sessions[recipient]
                except KeyError:
                    session = MancifySSHSession(
                        self.sms, sender, recipient,
                        self.connect_timeout, self.exec_timeout)
                    self.sessions[recipient] = session
                session.timestamp = time.time()
            session.execute(content)
        except Exception as e:
            msg = str(e)
            if len(msg) > 140:
                msg = msg[:137] + '...'
            self.sms.send(sender, recipient, msg)
        raise exc.HTTPOk('Message processed')
Beispiel #11
0
class BaseMqttReactor(MqttMessages):
    """
    Base class for MQTT-based plugins.
    """
    def __init__(self,
                 host='localhost',
                 port=1883,
                 keepalive=60,
                 base="microdrop"):
        self._host = host
        self._port = port
        self._keepalive = keepalive
        self.mqtt_client = mqtt.Client(client_id=self.client_id)
        self.mqtt_client.on_connect = self.on_connect
        self.mqtt_client.on_disconnect = self.on_disconnect
        self.mqtt_client.on_message = self.on_message
        self.should_exit = False
        self.router = PathRouter()
        self.subscriptions = []
        self.base = base

    ###########################################################################
    # Attributes
    # ==========
    @property
    def host(self):
        return self._host

    @host.setter
    def host(self, value):
        self._host = value
        self._connect()

    @property
    def port(self):
        return self._port

    @port.setter
    def port(self, value):
        self._port = value
        self._connect()

    @property
    def keepalive(self):
        return self._keepalive

    @keepalive.setter
    def keepalive(self, value):
        self._keepalive = value
        self._connect()

    @property
    def plugin_path(self):
        """Get parent directory of class location"""
        return os.path.dirname(
            os.path.realpath(inspect.getfile(self.__class__)))

    @property
    def plugin_name(self):
        """Get plugin name via the basname of the plugin path """
        return os.path.basename(self.plugin_path)

    @property
    def url_safe_plugin_name(self):
        """Make plugin name safe for mqtt and http requests"""
        return six.moves.urllib.parse.quote_plus(self.plugin_name)

    @property
    def client_id(self):
        """ ID used for mqtt client """
        return (self.url_safe_plugin_name + ">>" + self.plugin_path + ">>" +
                datetime.datetime.now().isoformat().replace(">>", ""))

    def addGetRoute(self, route, handler):
        """Adds route along with corresponding subscription"""
        self.router.add_route(route, handler)
        # Replace characters between curly brackets with "+" wildcard
        self.subscriptions.append(re.sub(r"\{(.+?)\}", "+", route))

    def sendMessage(self, topic, msg, retain=False, qos=0, dup=False):
        message = json.dumps(msg, cls=PandasJsonEncoder)
        self.mqtt_client.publish(topic, message, retain=retain, qos=qos)

    def subscribe(self):
        for subscription in self.subscriptions:
            self.mqtt_client.subscribe(subscription)

    ###########################################################################
    # Private methods
    # ===============
    def _connect(self):
        try:
            # Connect to MQTT broker.
            # TODO: Make connection parameters configurable.
            self.mqtt_client.connect(host=self.host,
                                     port=self.port,
                                     keepalive=self.keepalive)
        except socket.error:
            pass
            # logger.error('Error connecting to MQTT broker.')

    ###########################################################################
    # MQTT client handlers
    # ====================
    def on_connect(self, client, userdata, flags, rc):
        self.addGetRoute("microdrop/" + self.url_safe_plugin_name + "/exit",
                         self.exit)
        self.listen()
        self.subscribe()

    def on_disconnect(self, *args, **kwargs):
        # Startup Mqtt Loop after disconnected (unless should terminate)
        if self.should_exit:
            sys.exit()
        self._connect()
        self.mqtt_client.loop_forever()

    def on_message(self, client, userdata, msg):
        '''
        Callback for when a ``PUBLISH`` message is received from the broker.
        '''
        method, args = self.router.match(msg.topic)

        try:
            payload = json.loads(msg.payload, object_hook=pandas_object_hook)
        except ValueError:
            print("Message contains invalid json")
            print("topic: " + msg.topic)
            payload = None

        if method:
            method(payload, args)

    ###########################################################################
    # Control API
    # ===========
    def start(self):
        # Connect to MQTT broker.
        self._connect()
        # Start loop in background thread.
        signal.signal(signal.SIGINT, self.exit)
        self.mqtt_client.loop_forever()

    def exit(self, a=None, b=None):
        self.should_exit = True
        self.mqtt_client.disconnect()

    def stop(self):
        '''
        Stop plugin thread.
        '''
        # Stop client loop background thread (if running).
        self.mqtt_client.loop_stop()
Beispiel #12
0
class PicroscopyWsgiApp(object):
    def __init__(self, **kwargs):
        super().__init__()
        self.library = PicroscopyLibrary(**kwargs)
        self.helpers = WebHelpers(self.library)
        self.clients = kwargs.get('clients', IPv4Network('0.0.0.0/0'))
        logging.info('Clients must be on network %s', self.clients)
        self.static_dir = os.path.abspath(
            os.path.normpath(
                kwargs.get('static_dir', os.path.join(HERE, 'static'))))
        logging.info('Static files: %s', self.static_dir)
        self.templates_dir = os.path.abspath(
            os.path.normpath(
                kwargs.get('templates_dir', os.path.join(HERE, 'templates'))))
        logging.info('Chameleon templates: %s', self.templates_dir)
        self.templates = PageTemplateLoader(self.templates_dir,
                                            default_extension='.pt')
        self.layout = self.templates['layout']
        # No need to make flashes a per-session thing - it's a single user app!
        self.flashes = []
        self.router = PathRouter()
        # XXX Add handler for exiting system
        # XXX Make exit code conditional? (upgrade/reboot/shutdown/etc.)
        self.router.add_routes([
            url('/', self.do_template, kwargs={'page': 'library'},
                name='home'),
            url('/{page}.html', self.do_template, name='template'),
            url('/view/{image}.html',
                self.do_template,
                kwargs={'page': 'image'},
                name='view'),
            url('/static/{path:any}', self.do_static, name='static'),
            url('/images/{image}', self.do_image, name='image'),
            url('/thumbs/{image}', self.do_thumb, name='thumb'),
            url('/delete/{image}', self.do_delete, name='delete'),
            url('/config', self.do_config, name='config'),
            url('/reset', self.do_reset, name='reset'),
            url('/capture', self.do_capture, name='capture'),
            url('/download', self.do_download, name='download'),
            url('/send', self.do_send, name='send'),
            url('/logout', self.do_logout, name='logout'),
        ])

    def __call__(self, environ, start_response):
        req = Request(environ)
        try:
            if not IPv4Address(req.remote_addr) in self.clients:
                raise exc.HTTPForbidden()
            handler, kwargs = self.router.match(req.path_info)
            if handler:
                # XXX Why does route_name only appear in kwargs sometimes?!
                if 'route_name' in kwargs:
                    del kwargs['route_name']
                resp = handler(req, **kwargs)
            else:
                self.not_found(req)
        except exc.HTTPException as e:
            # The exception itself is a WSGI response
            resp = e
        return resp(environ, start_response)

    def not_found(self, req):
        """
        Handler for unknown locations (404)
        """
        raise exc.HTTPNotFound('The resource at %s could not be found' %
                               req.path_info)

    def do_reset(self, req):
        """
        Reset all settings to their defaults
        """
        self.library.camera_reset()
        self.flashes.append('Camera settings reset to defaults')
        raise exc.HTTPFound(
            location=self.router.path_for('template', page='library'))

    def do_config(self, req):
        """
        Configure the library and camera settings
        """
        # Resolution is handled specially as the camera needs to stop the
        # preview in order to change it
        try:
            new_resolution = tuple(
                int(i) for i in req.params['resolution'].split('x', 1))
            if len(new_resolution) != 2:
                raise ValueError()
        except ValueError:
            self.flashes.append('Invalid resolution: %s' %
                                req.params['resolution'])
        if self.library.camera.resolution != new_resolution:
            try:
                self.library.camera.stop_preview()
                try:
                    self.library.camera.resolution = new_resolution
                finally:
                    self.library.camera.start_preview()
            except PiCameraError:
                self.flashes.append('Unable to change camera resolution '
                                    'to %s' % req.params['resolution'])
        # Everything else is handled generically...
        for setting in (
                'sharpness',
                'contrast',
                'brightness',
                'saturation',  #'ISO',
                'exposure-compensation'):
            try:
                setattr(self.library.camera, setting.replace('-', '_'),
                        int(req.params[setting]))
            except ValueError:
                self.flashes.append('Invalid %s: %s' %
                                    (setting, req.params[setting]))
        for setting in ('hflip', 'vflip'):
            try:
                setattr(self.library.camera, setting.replace('-', '_'),
                        bool(req.params.get(setting, 0)))
            except ValueError:
                self.flashes.append('Invalid %s: %s' %
                                    (setting, req.params[setting]))
        for setting in ('meter-mode', 'awb-mode', 'exposure-mode'):
            try:
                setattr(self.library.camera, setting.replace('-', '_'),
                        req.params[setting])
            except ValueError:
                self.flashes.append('Invalid %s: %s' %
                                    (setting, req.params[setting]))
        for setting in ('artist', 'email', 'copyright', 'description',
                        'filename-template', 'format'):
            try:
                setattr(self.library, setting.replace('-', '_'),
                        req.params[setting])
            except ValueError:
                self.flashes.append('Invalid %s: %s' %
                                    (setting, req.params[setting]))
        # If any settings failed, re-render the settings form
        if self.flashes:
            return self.do_template(req, 'settings')
        raise exc.HTTPFound(
            location=self.router.path_for('template', page='library'))

    def do_capture(self, req):
        """
        Take a new image with the camera and add it to the library
        """
        self.library.capture()
        raise exc.HTTPFound(
            location=self.router.path_for('template', page='library'))

    def do_download(self, req):
        """
        Send the library as a .zip archive
        """
        archive = self.library.archive()
        size = archive.seek(0, io.SEEK_END)
        archive.seek(0)
        resp = Response()
        resp.content_type = 'application/zip'
        resp.content_length = size
        resp.content_disposition = 'attachment; filename=images.zip'
        resp.app_iter = FileWrapper(archive)
        return resp

    def do_send(self, req):
        """
        Send the library as a set of attachments to an email
        """
        self.library.send()
        self.flashes.append('Email sent to %s' % library.email)
        raise exc.HTTPFound(
            location=self.router.path_for('template', page='library'))

    def do_delete(self, req, image):
        """
        Delete the selected images from library
        """
        self.library.remove(image)
        raise exc.HTTPFound(
            location=self.router.path_for('template', page='library'))

    def do_logout(self, req):
        """
        Clear the library of all images, reset all settings
        """
        self.library.clear()
        self.library.user_reset()
        self.library.camera_reset()
        raise exc.HTTPFound(
            location=self.router.path_for('template', page='settings'))

    def do_image(self, req, image):
        """
        Serve an image from the library
        """
        if not image in self.library:
            self.not_found(req)
        resp = Response()
        resp.content_type, resp.content_encoding = mimetypes.guess_type(
            image, strict=False)
        resp.content_length = self.library.stat_image(image).st_size
        resp.app_iter = FileWrapper(self.library.open_image(image))
        return resp

    def do_thumb(self, req, image):
        """
        Serve a thumbnail of an image from the library
        """
        if not image in self.library:
            self.not_found(req)
        resp = Response()
        resp.content_type = 'image/jpeg'
        resp.content_length = self.library.stat_thumbnail(image).st_size
        resp.app_iter = FileWrapper(self.library.open_thumbnail(image))
        return resp

    def do_static(self, req, path):
        """
        Serve static files from disk
        """
        path = os.path.normpath(os.path.join(self.static_dir, path))
        if not path.startswith(self.static_dir):
            self.not_found(req)
        resp = Response()
        resp.content_type, resp.content_encoding = mimetypes.guess_type(
            path, strict=False)
        if resp.content_type is None:
            resp.content_type = 'application/octet-stream'
        resp.content_length = os.stat(path).st_size
        resp.app_iter = FileWrapper(io.open(path, 'rb'))
        return resp

    def do_template(self, req, page, image=None):
        """
        Serve a Chameleon template-based page
        """
        resp = Response()
        resp.content_type = 'text/html'
        resp.content_encoding = 'utf-8'
        try:
            template = self.templates[page]
        except ValueError:
            self.not_found(req)
        resp.text = template(req=req,
                             page=page,
                             image=image,
                             helpers=self.helpers,
                             layout=self.layout,
                             flashes=self.flashes,
                             library=self.library,
                             camera=self.library.camera,
                             router=self.router)
        del self.flashes[:]
        return resp
Beispiel #13
0
import json
from io import BytesIO
from functools import partial
from urllib import parse
from rit.core.web.wsgi import get_application
from rit.core.web.urls import all_urls
from wheezy.http.response import HTTP_STATUS
from wheezy.http import HTTPResponse, HTTPRequest
from wheezy.routing import PathRouter
from rit.app.conf import settings

application = get_application()

router = PathRouter()

for url in all_urls:
    router.add_route(*url)

HTTP_STATUS_STRING_TO_CODE = dict(zip(HTTP_STATUS.values(), HTTP_STATUS.keys()))


class FakePayload(object):
    """
    A wrapper around BytesIO that restricts what can be read since data from
    the network can't be seeked and cannot be read outside of its content_type    length. This makes sure that views can't do anything under the test client
    that wouldn't work in Real Life.
    """
    def __init__(self, content=None):
        self.__content = BytesIO()
        self.__len = 0
        self.read_started = False
Beispiel #14
0
class PicroscopyWsgiApp(object):
    def __init__(self, **kwargs):
        super().__init__()
        self.library = PicroscopyLibrary(**kwargs)
        self.helpers = WebHelpers(self.library)
        self.clients = kwargs.get('clients', IPv4Network('0.0.0.0/0'))
        logging.info('Clients must be on network %s', self.clients)
        self.static_dir = os.path.abspath(os.path.normpath(kwargs.get(
            'static_dir', os.path.join(HERE, 'static')
            )))
        logging.info('Static files: %s', self.static_dir)
        self.templates_dir = os.path.abspath(os.path.normpath(kwargs.get(
            'templates_dir', os.path.join(HERE, 'templates')
            )))
        logging.info('Chameleon templates: %s', self.templates_dir)
        self.templates = PageTemplateLoader(
            self.templates_dir, default_extension='.pt')
        self.layout = self.templates['layout']
        # No need to make flashes a per-session thing - it's a single user app!
        self.flashes = []
        self.router = PathRouter()
        # XXX Add handler for exiting system
        # XXX Make exit code conditional? (upgrade/reboot/shutdown/etc.)
        self.router.add_routes([
            url('/',                   self.do_template, kwargs={'page': 'library'}, name='home'),
            url('/{page}.html',        self.do_template, name='template'),
            url('/view/{image}.html',  self.do_template, kwargs={'page': 'image'}, name='view'),
            url('/static/{path:any}',  self.do_static,   name='static'),
            url('/images/{image}',     self.do_image,    name='image'),
            url('/thumbs/{image}',     self.do_thumb,    name='thumb'),
            url('/delete/{image}',     self.do_delete,   name='delete'),
            url('/config',             self.do_config,   name='config'),
            url('/reset',              self.do_reset,    name='reset'),
            url('/capture',            self.do_capture,  name='capture'),
            url('/download',           self.do_download, name='download'),
            url('/send',               self.do_send,     name='send'),
            url('/logout',             self.do_logout,   name='logout'),
            ])

    def __call__(self, environ, start_response):
        req = Request(environ)
        try:
            if not IPv4Address(req.remote_addr) in self.clients:
                raise exc.HTTPForbidden()
            handler, kwargs = self.router.match(req.path_info)
            if handler:
                # XXX Why does route_name only appear in kwargs sometimes?!
                if 'route_name' in kwargs:
                    del kwargs['route_name']
                resp = handler(req, **kwargs)
            else:
                self.not_found(req)
        except exc.HTTPException as e:
            # The exception itself is a WSGI response
            resp = e
        return resp(environ, start_response)

    def not_found(self, req):
        """
        Handler for unknown locations (404)
        """
        raise exc.HTTPNotFound(
            'The resource at %s could not be found' % req.path_info)

    def do_reset(self, req):
        """
        Reset all settings to their defaults
        """
        self.library.camera_reset()
        self.flashes.append('Camera settings reset to defaults')
        raise exc.HTTPFound(
            location=self.router.path_for('template', page='library'))

    def do_config(self, req):
        """
        Configure the library and camera settings
        """
        # Resolution is handled specially as the camera needs to stop the
        # preview in order to change it
        try:
            new_resolution = tuple(
                int(i) for i in req.params['resolution'].split('x', 1))
            if len(new_resolution) != 2:
                raise ValueError()
        except ValueError:
            self.flashes.append(
                'Invalid resolution: %s' % req.params['resolution'])
        if self.library.camera.resolution != new_resolution:
            try:
                self.library.camera.stop_preview()
                try:
                    self.library.camera.resolution = new_resolution
                finally:
                    self.library.camera.start_preview()
            except PiCameraError:
                self.flashes.append(
                    'Unable to change camera resolution '
                    'to %s' % req.params['resolution'])
        # Everything else is handled generically...
        for setting in (
                'sharpness', 'contrast', 'brightness', 'saturation', #'ISO',
                'exposure-compensation'):
            try:
                setattr(
                    self.library.camera, setting.replace('-', '_'),
                    int(req.params[setting])
                    )
            except ValueError:
                self.flashes.append(
                    'Invalid %s: %s' % (setting, req.params[setting]))
        for setting in ('hflip', 'vflip'):
            try:
                setattr(
                    self.library.camera, setting.replace('-', '_'),
                    bool(req.params.get(setting, 0))
                    )
            except ValueError:
                self.flashes.append(
                    'Invalid %s: %s' % (setting, req.params[setting]))
        for setting in ('meter-mode', 'awb-mode', 'exposure-mode'):
            try:
                setattr(
                    self.library.camera, setting.replace('-', '_'),
                    req.params[setting]
                    )
            except ValueError:
                self.flashes.append(
                    'Invalid %s: %s' % (setting, req.params[setting]))
        for setting in (
                'artist', 'email', 'copyright', 'description',
                'filename-template', 'format'):
            try:
                setattr(
                    self.library, setting.replace('-', '_'),
                    req.params[setting]
                    )
            except ValueError:
                self.flashes.append(
                    'Invalid %s: %s' % (setting, req.params[setting]))
        # If any settings failed, re-render the settings form
        if self.flashes:
            return self.do_template(req, 'settings')
        raise exc.HTTPFound(
            location=self.router.path_for('template', page='library'))

    def do_capture(self, req):
        """
        Take a new image with the camera and add it to the library
        """
        self.library.capture()
        raise exc.HTTPFound(
            location=self.router.path_for('template', page='library'))

    def do_download(self, req):
        """
        Send the library as a .zip archive
        """
        archive = self.library.archive()
        size = archive.seek(0, io.SEEK_END)
        archive.seek(0)
        resp = Response()
        resp.content_type = 'application/zip'
        resp.content_length = size
        resp.content_disposition = 'attachment; filename=images.zip'
        resp.app_iter = FileWrapper(archive)
        return resp

    def do_send(self, req):
        """
        Send the library as a set of attachments to an email
        """
        self.library.send()
        self.flashes.append('Email sent to %s' % library.email)
        raise exc.HTTPFound(
            location=self.router.path_for('template', page='library'))

    def do_delete(self, req, image):
        """
        Delete the selected images from library
        """
        self.library.remove(image)
        raise exc.HTTPFound(
            location=self.router.path_for('template', page='library'))

    def do_logout(self, req):
        """
        Clear the library of all images, reset all settings
        """
        self.library.clear()
        self.library.user_reset()
        self.library.camera_reset()
        raise exc.HTTPFound(
            location=self.router.path_for('template', page='settings'))

    def do_image(self, req, image):
        """
        Serve an image from the library
        """
        if not image in self.library:
            self.not_found(req)
        resp = Response()
        resp.content_type, resp.content_encoding = mimetypes.guess_type(
                image, strict=False)
        resp.content_length = self.library.stat_image(image).st_size
        resp.app_iter = FileWrapper(self.library.open_image(image))
        return resp

    def do_thumb(self, req, image):
        """
        Serve a thumbnail of an image from the library
        """
        if not image in self.library:
            self.not_found(req)
        resp = Response()
        resp.content_type = 'image/jpeg'
        resp.content_length = self.library.stat_thumbnail(image).st_size
        resp.app_iter = FileWrapper(self.library.open_thumbnail(image))
        return resp

    def do_static(self, req, path):
        """
        Serve static files from disk
        """
        path = os.path.normpath(os.path.join(self.static_dir, path))
        if not path.startswith(self.static_dir):
            self.not_found(req)
        resp = Response()
        resp.content_type, resp.content_encoding = mimetypes.guess_type(
                path, strict=False)
        if resp.content_type is None:
            resp.content_type = 'application/octet-stream'
        resp.content_length = os.stat(path).st_size
        resp.app_iter = FileWrapper(io.open(path, 'rb'))
        return resp

    def do_template(self, req, page, image=None):
        """
        Serve a Chameleon template-based page
        """
        resp = Response()
        resp.content_type = 'text/html'
        resp.content_encoding = 'utf-8'
        try:
            template = self.templates[page]
        except ValueError:
            self.not_found(req)
        resp.text = template(
            req=req,
            page=page,
            image=image,
            helpers=self.helpers,
            layout=self.layout,
            flashes=self.flashes,
            library=self.library,
            camera=self.library.camera,
            router=self.router)
        del self.flashes[:]
        return resp
Beispiel #15
0
class MqttClient(MqttMessages):
    """
       Example MqttClient for Application Frameworks (such as Microdrop)
       Used with the following broker:
       https://github.com/sci-bots/microdrop-3.0/blob/master/MoscaServer.js
    """
    def __init__(self, host="localhost", port=1883, base="microdrop"):
        super().__init__()
        self.router = PathRouter()
        self.host = host
        self.port = port
        self.base = base
        self.subscriptions = []
        self.client_id = self.ClientID()
        self.client = self.Client()
        self.connected = False

    @property
    def filepath(self):
        try:
            return os.path.dirname(inspect.getfile(self.__class__))
        except:
            return 'unknown'

    @property
    def name(self):
        safe_chars = '~@#$&()*!+=:;,.?/\''
        return urllib.parse.quote(camelToSnake(self.__class__.__name__),
                                  safe=safe_chars)

    @property
    def version(self):
        return '0.0'

    def send_message(self, topic, msg={}, retain=False, qos=0, dup=False):
        message = json.dumps(msg)
        self.client.publish(topic, message, retain=retain, qos=qos)

    def on_connect(self, client, userdata, flags, rc):
        self.connected = True
        self.listen()
        self.trigger('start', 'null')

    def on_disconnect(self, client, userdata, rc):
        self.connected = False

    def listen(self):
        print(f'No listen method implemented for {self.name}')

    def on_message(self, client, userdata, msg):
        method, args = self.router.match(msg.topic)

        try:
            payload = json.loads(msg.payload)
        except ValueError:
            print("Message contains invalid json")
            print(f'topic: {msg.topic}')
            payload = None

        if method:
            method(payload, args)

    def wrap_data(self, key, val):
        msg = {}
        if isinstance(val, dict) and val is not None:
            msg = val
        else:
            msg[key] = val
        msg['__head__'] = self.DefaultHeader()
        return msg

    def Client(self, keepalive=60):
        client = mqtt.Client(self.client_id)
        client.on_connect = self.on_connect
        client.on_message = self.on_message
        client.on_disconnect = self.on_disconnect
        client.connect(host=self.host, port=self.port)
        client.loop_start()
        return client

    def ClientID(self):
        timestamp = str(time.time()).replace(".", "")
        randnum = random.randint(1, 1000)
        return f'{self.name}>>{self.filepath}>>{timestamp}.{randnum}'

    def DefaultHeader(self):
        header = {}
        header['plugin_name'] = self.name
        header['plugin_version'] = self.version
        return header
Beispiel #16
0
class MicropedeClient(Topics):
    """
       Python based client for Micropede Application Framework
       Used with the following broker:
       https://github.com/The-Brainery/SciCAD/blob/master/MoscaServer.js
    """
    def __init__(self,
                 app_name,
                 host="localhost",
                 port=None,
                 name=None,
                 version='0.0.0',
                 loop=None):
        if (app_name is None):
            raise ("app_name is undefined")

        if (port is None):
            port = 1884

        if (name is None):
            name = get_class_name(self)

        self.router = PathRouter()
        client_id = generate_client_id(name, app_name)
        self.__listen = _.noop

        self.app_name = app_name
        self.client_id = client_id
        self.name = name
        self.schema = {}
        self.subscriptions = []
        self.host = host
        self.port = port
        self.version = version
        self.last_message = None
        self.loop = None
        self.safe = None
        self.client = None

        if (loop == None):
            # Create thread to run event loop
            def start_loop(x):
                x.loop = asyncio.new_event_loop()
                x.loop.call_soon_threadsafe(x.ready_event.set)
                x.loop.run_forever()

            # Initialize loop and pass reference to main thread
            class X(object):
                pass

            X.ready_event = threading.Event()
            t = Thread(target=start_loop, args=(X, ))
            t.start()
            X.ready_event.wait()
            self.loop = X.loop
        else:
            self.loop = loop
        self.safe = safe(self.loop)

        # Start client
        self.wait_for(self.connect_client(client_id, host, port))

    def wait_for(self, f):
        if (isinstance(f, (asyncio.Future, types.CoroutineType))):
            asyncio.ensure_future(f, loop=self.loop)

    def wrap(self, func):
        return lambda *args, **kwargs: self.wait_for(func(*args, **kwargs))

    @property
    def is_plugin(self):
        return not _.is_equal(self.listen, _.noop)

    @property
    def listen(self):
        return self.__listen

    @listen.setter
    def listen(self, val):
        self.__listen = val

    def exit(self, *args, **kwargs):
        pass

    def add_binding(self, channel, event, retain=False, qos=0, dup=False):
        return self.on(
            event, lambda d: self.send_message(channel, d, retain, qos, dup))

    def get_schema(self, payload, name):
        LABEL = f'{self.app_name}::get_schema'
        return self.notify_sender(payload, self.schema, 'get-schema')

    def validate_schema(self, payload):
        return validate(payload, self.schema)

    def ping(self, payload, params):
        return self.notify_sender(payload, "pong", "ping")

    def add_subscription(self, channel, handler):
        path = channel_to_route_path(channel)
        sub = channel_to_subscription(channel)
        route_name = f'{uuid.uuid1()}-{uuid.uuid4()}'
        future = asyncio.Future(loop=self.loop)

        try:
            if self.client._state != mqtt_cs_connected:
                if (future.done() == False):
                    future.set_exception(
                        Exception(
                            f'Failed to add subscription. ' +
                            + 'Client is not connected {self.name}, {self.channel}'
                        ))
                return future

            def add_sub(*args, **kwargs):
                self.client.on_unsubscribe = _.noop

                def on_sub(client, userdata, mid, granted_qos):
                    self.client.on_subscribe = _.noop
                    if (future.done() == False):
                        future.set_result('done')

                self.client.on_subscribe = self.safe(on_sub)
                self.client.subscribe(sub)

            if sub in self.subscriptions:
                self.client.on_unsubscribe = self.safe(add_sub)
                self.client.unsubscribe(sub)
            else:
                self.subscriptions.append(sub)
                self.router.add_route(path, self.wrap(handler))
                add_sub()

        except Exception as e:
            if (future.done() == False):
                future.set_exception(e)

        return future

    def remove_subscription(self, channel):
        sub = channel_to_subscription(channel)
        future = asyncio.Future(loop=self.loop)

        def on_unsub(client, userdata, mid):
            self.client.on_unsubscribe = _.noop
            _.pull(self.subscriptions, sub)
            if (future.done() == False):
                future.set_result('done')

        self.client.unsubscribe(sub)
        return future

    def _get_subscriptions(self, payload, name):
        LABEL = f'{self.app_name}::get_subscriptions'
        return self.notify_sender(payload, self.subscriptions,
                                  'get-subscriptions')

    def notify_sender(self, payload, response, endpoint, status='success'):
        if (status != 'success'):
            response = _.flatten_deep(response)
        receiver = get_receiver(payload)

        self.send_message(
            f'{self.app_name}/{self.name}/notify/{receiver}/{endpoint}',
            wrap_data(None, {
                'status': status,
                'response': response
            }, self.name, self.version))

        return response

    def connect_client(self, client_id, host, port, timeout=DEFAULT_TIMEOUT):
        self.client = mqtt.Client(client_id)
        future = asyncio.Future(loop=self.loop)

        def on_connect(client, userdata, flags, rc):
            if future.done():
                return

            self.subscriptions = []

            if self.is_plugin:

                def on_done_1(d):
                    def on_done_2(d):
                        if future.done():
                            return
                        self.listen()
                        self.default_sub_count = len(self.subscriptions)
                        self.client.on_disconnect = self.safe(self.exit)
                        future.set_result('done')

                    f2 = self.on_trigger_msg("exit", self.safe(self.exit))
                    f2.add_done_callback(self.safe(on_done_2))

                # TODO: Run futures sequentially
                self.on_trigger_msg("ping", self.safe(self.ping))
                self.on_trigger_msg("update-version",
                                    self.safe(self.update_version))
                f = self.on_trigger_msg("get-schema",
                                        self.safe(self.get_schema))
                self.wait_for(self.set_state('schema', self.schema))
                f1 = self.on_trigger_msg("get-subscriptions",
                                         self.safe(self._get_subscriptions))
                f1.add_done_callback(self.safe(on_done_1))
            else:
                self.listen()
                self.default_sub_count = 0
                if (future.done() == False):
                    future.set_result('done')

        self.client.on_connect = self.safe(on_connect)
        self.client.on_message = self.safe(self.on_message)
        self.client.connect(host=self.host, port=self.port)

        self.client.loop_start()

        def on_timeout():
            if (future.done() == False):
                future.set_exception(Exception(f'timeout {timeout}ms'))

        set_timeout(self.safe(on_timeout), timeout)
        return future

    def disconnect_client(self, timeout=DEFAULT_TIMEOUT):
        future = asyncio.Future(loop=self.loop)
        self.subscriptions = []
        self.router = PathRouter()

        def off():
            if hasattr(self, '_on_off_events'):
                del self._on_off_events

        if (_.get(self, 'client._state') != mqtt_cs_connected):
            off()
            if (hasattr(self, 'client')):
                del self.client
            future.set_result('done')
            return future
        else:

            def on_disconnect(*args, **kwargs):
                if future.done():
                    return
                off()
                if (hasattr(self, 'client')):
                    del self.client

                future.set_result('done')

            if hasattr(self, 'client'):
                self.client.on_disconnect = self.safe(on_disconnect)
                self.client.disconnect()
                set_timeout(self.safe(on_disconnect), timeout)
            else:
                if (future.done() == False):
                    future.set_result('done')

        return future

    def on_message(self, client, userdata, msg):
        try:
            payload = json.loads(msg.payload)
        except ValueError:
            print("Message contains invalid json")
            print(f'topic: {msg.topic}')
            payload = None

        topic = msg.topic
        if (topic is None or topic is ''):
            return

        method, args = self.router.match(topic)
        if method:
            method(payload, args)

    def send_message(self,
                     topic,
                     msg={},
                     retain=False,
                     qos=0,
                     dup=False,
                     timeout=DEFAULT_TIMEOUT):
        future = asyncio.Future(loop=self.loop)
        if (_.is_dict(msg) and _.get(msg, '__head__') is None):
            head = wrap_data(None, None, self.name, self.version)['__head__']
            _.set_(msg, '__head__', head)
        message = json.dumps(msg)

        _mid = None

        def on_publish(client, userdata, mid):
            if (mid == _mid):
                if (future.done() == False):
                    future.set_result('done')

        def on_timeout():
            if (future.done() == False):
                future.set_exception(Exception(f'timeout {timeout}ms'))

        self.client.on_publish = self.safe(on_publish)
        (qos, _mid) = self.client.publish(topic,
                                          payload=message,
                                          qos=qos,
                                          retain=retain)
        set_timeout(self.safe(on_timeout), timeout)

        return future

    async def dangerously_set_state(self, key, value, plugin):
        """
        Dangerously set the state of another plugin (skip validation)
        key: str
        value: any
        plugin: str
        """
        plugin = plugin or self.name
        topic = f'{self.app_name}/{plugin}/state/{key}'
        await self.send_message(topic, value, True, 0, False)

    async def set_state(self, key, value):
        topic = f'{self.app_name}/{self.name}/state/{key}'
        await self.send_message(topic, value, True, 0, False)

    def update_version(self, payload, params):
        try:
            state = payload["state"]
            storage_version = payload["storageVersion"]
            plugin_version = payload["pluginVersion"]
            state = self._update_version(state, storage_version,
                                         plugin_version)
            return self.notify_sender(payload, state, "update-version")
        except Exception as e:
            stack = dump_stack(self.name, e)
            return self.notify_sender(payload, stack, "update-version",
                                      "failed")

    @staticmethod
    def _version(cls):
        # Override Me!
        return "0.0.0"

    @staticmethod
    def _update_version(cls, state, storageVersion, pluginVersion):
        # Override Me!
        return state
Beispiel #17
0
import json
from io import BytesIO
from functools import partial
from urllib import parse
from rit.core.web.wsgi import get_application
from rit.core.web.urls import all_urls
from wheezy.http.response import HTTP_STATUS
from wheezy.http import HTTPResponse, HTTPRequest
from wheezy.routing import PathRouter
from rit.app.conf import settings

application = get_application()

router = PathRouter()

for url in all_urls:
    router.add_route(*url)

HTTP_STATUS_STRING_TO_CODE = dict(zip(HTTP_STATUS.values(),
                                      HTTP_STATUS.keys()))


class FakePayload(object):
    """
    A wrapper around BytesIO that restricts what can be read since data from
    the network can't be seeked and cannot be read outside of its content_type    length. This makes sure that views can't do anything under the test client
    that wouldn't work in Real Life.
    """
    def __init__(self, content=None):
        self.__content = BytesIO()
        self.__len = 0
class PololuRobotWebControlApp(object):
  def __init__(self, **kwargs):
    self.form = WebControlFormHelper(**kwargs)
    self.logger = kwargs.get('logger',)
    self.robot = kwargs.get('robot')
    #initialize speed in form with robot speed
    self.form.speed = self.robot.setDriveSpeed
    #create routes to web pages
    self.router = PathRouter()
    self.router.add_routes([
      url('/', self.do_main_page, name='home'),
      url('/doRobotControl', self.do_process_form, name='execute'),
      url('/showRobotControlForm', self.do_display_form, name='view')
      ])

  def __call__(self, environ, start_response):
    req = Request(environ)
    try:
      handler, kwargs = self.router.match(req.path_info)
      if handler:
        # XXX Why does route_name only appear in kwargs sometimes?!
        if 'route_name' in kwargs:
            del kwargs['route_name']
        resp = handler(req, **kwargs)
      else:
        self.not_found(req)
    except exc.HTTPException as e:
      # The exception itself is a WSGI response
      resp = e
    return resp(environ, start_response)

  def not_found(self, req):
    """Handler for unknown locations (404)"""
    raise exc.HTTPNotFound('The resource at %s could not be found' % req.path_info)


  #------------------------------------------------------------------------------#
  # do_main_page: load the mainpage. The main page contains an iFrame which      #
  #               triggers the consecutive loading of the control form           #
  #               The main page contains a link to mjpgStreamServer, which shows #
  #               a video stream from the attached raspicam. The url for this    #
  #               server is configured in etc/config.ini                         #
  #------------------------------------------------------------------------------#
  # version who when       description                                           #
  # 1.00    hta 15.05.2014 Initial version                                       #
  #------------------------------------------------------------------------------# 
  def do_main_page(self, req):
    res = Response()
    res.content_type = 'text/html'
    res.text         = self.form.mainPage % {'mjpgStreamServer':self.form.mjpgStreamServer}
    return res

  #------------------------------------------------------------------------------#
  # do_display_form: the control form, and set the color of the button text and  #
  #                  set the message to be shown to the user (if any)            #
  # parameters: message: Text message to be shown to user                        #
  #             speed  : Speed, value for the slider bar                         #
  #             toggleRovingButtonText: Roving OFF, Roving ON                   #
  #             backwardButtonColor, forwardButtonColor, leftButtonColor,        #
  #             rightButtonColor, stopButtonColor, toggleRovingButtonColor:     #
  #                      Color for the text on the control buttons. The purpose  #
  #                      is to highlight the currently active action suchs as    #
  #                      forward, left etc.                                      #
  #------------------------------------------------------------------------------#
  # version who when       description                                           #
  # 1.00    hta 15.05.2014 Initial version                                       #
  #------------------------------------------------------------------------------# 
  def do_display_form(self, req):
    res = Response()
    res.content_type = 'text/html'
    res.text         = self.form.controlForm % {'message':self.form.message, 
                                    'speed':  self.form.speed, 
                                    'backwardButtonColor':     self.form.backwardButtonColor, 
                                    'forwardButtonColor':      self.form.forwardButtonColor, 
                                    'leftButtonColor':         self.form.leftButtonColor, 
                                    'rightButtonColor':        self.form.rightButtonColor, 
                                    'stopButtonColor':         self.form.stopButtonColor,
                                    'toggleRovingButtonColor': self.form.toggleRovingButtonColor,
                                    'toggleRovingButtonText':  self.form.toggleRovingButtonText}   
    return res
    
  #------------------------------------------------------------------------------#
  # process_form: Processes the user input from the control form, executes the   #
  #               desired action (e.g. forward, backward) and give feedback to   #
  #               the user indicating what action has been taken                 #
  #                  is highlighted                                              #
  # parameters: params: dictionary object containing parameters and their values #
  #                     from the form.                                           #
  #                     action: possbile values: forward, backward, left, right, #
  #                                              stop, setSpeed                  #
  #                     speed:  possible values: 0-127
  #------------------------------------------------------------------------------#
  # version who when       description                                           #
  # 1.00    hta 15.05.2014 Initial version                                       #
  #------------------------------------------------------------------------------# 
  def do_process_form(self, req):
    
    action           = req.params['action'] 
    speedSliderValue = req.params['speed']
    self.logger.debug('action['+action+'] speedSliderValue['+speedSliderValue+']')
    if action == 'forward':
      self.form.message='Going '+action
      self.form.setButtonColors(backward=False, forward=True, left=False, right=False, stop=False, roving=False)
      self.robot.stopRoving()
      self.robot.driveForwards()
    elif action == 'backward':
      self.form.message='Going '+action
      self.form.setButtonColors(backward=True, forward=False, left=False, right=False, stop=False, roving=False)
      self.robot.stopRoving()
      self.robot.driveBackwards()
    elif action == 'left':
      self.form.message='Turning '+action    
      self.form.setButtonColors(backward=False, forward=False, left=True, right=False, stop=False, roving=False)
      self.robot.stopRoving()
      self.robot.turnLeft()
    elif action == 'right':
      self.form.message='Turning '+action   
      self.form.setButtonColors(backward=False, forward=False, left=False, right=True, stop=False, roving=False)
      self.robot.stopRoving()
      self.robot.turnRight()
    elif action == 'setSpeed':
      self.form.message='Setting speed to '+speedSliderValue
      self.form.speed = int(speedSliderValue)
      self.robot.setSpeed(self.form.speed)
    elif action == 'stop':
      self.form.message='Stopping'  
      self.form.setButtonColors(backward=False, forward=False, left=False, right=False, stop=True, roving=False)
      self.robot.stop()
      if self.robot.isRoving:
        self.robot.stopRoving()
    elif action == 'roving':
      #Toggle roving, of roving on then turn it of and vice versa
      if self.robot.isRoving:
        self.form.message='End roving'  
        self.form.setButtonColors(backward=False, forward=False, left=False, right=False, stop=False, roving=False)
        self.robot.stopRoving()
      else:
        self.form.message='Start roving'  
        self.form.setButtonColors(backward=False, forward=False, left=False, right=False, stop=False, roving=True)      
        self.robot.runRoving()
    #return updated control form to web client  
    return self.do_display_form (req)