summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorMagnus Feuer <mfeuer@jaguarlandrover.com>2015-02-12 11:32:59 -0800
committerMagnus Feuer <mfeuer@jaguarlandrover.com>2015-02-12 11:32:59 -0800
commitc3804481dc7f615584f74fb94626b7f900a4ce24 (patch)
tree059c9f191d08d85e8399f7ecac41508351f31818 /python
parent8586ccb7554c19b6ebe8eadf4fd622b68f5fced7 (diff)
downloadrvi_core-c3804481dc7f615584f74fb94626b7f900a4ce24.tar.gz
Added simplified RVI integration
Diffstat (limited to 'python')
-rw-r--r--python/README.md48
-rwxr-xr-xpython/rvi_call.py67
-rwxr-xr-xpython/rvi_service.py99
-rw-r--r--python/rvilib.py144
4 files changed, 358 insertions, 0 deletions
diff --git a/python/README.md b/python/README.md
new file mode 100644
index 0000000..a768d1f
--- /dev/null
+++ b/python/README.md
@@ -0,0 +1,48 @@
+Copyright (C) 2014, Jaguar Land Rover
+
+This document is licensed under Creative Commons
+Attribution-ShareAlike 4.0 International.
+
+
+# RVI PYTHON INTEGRATION
+
+This directory contains the ```rvilib.py``` file necessary to have
+your python code send and receive RVI service invocations.
+
+Also included are two test programs that use ```rvilib.py``` :
+
+1. ```rvi_service.py```<br>
+Registers a single service with an RVI node and then prints out
+a message when that service is invoked.
+
+2. ```rvi_call.py```<br>
+Invokes a service registered in an RVI network.
+
+These two commands can be used to quickly test connectivity between
+various RVI nodes.
+
+# SETUP
+In order for ```rvilib.py``` to work, it needs jsonrplib installed from
+
+[https://github.com/joshmarshall/jsonrpclib](https://github.com/joshmarshall/jsonrpclib).
+
+Install by using one of the following commands:
+
+ easy_install jsonrpclib
+
+or
+
+ pip install jsonrpclib
+
+
+You can use rvilib.py by simply copying it into your project.
+
+# CALLING A SERVICE
+
+See ```rvi_call.py``` for how to invoke RVI-based services.
+
+# IMPLEMENTING A SERVICE
+
+See ```rvi_service.py``` for how to implement and register RVI-based services.
+
+
diff --git a/python/rvi_call.py b/python/rvi_call.py
new file mode 100755
index 0000000..015f590
--- /dev/null
+++ b/python/rvi_call.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+
+#
+# Copyright (C) 2014, Jaguar Land Rover
+#
+# This program is licensed under the terms and conditions of the
+# Mozilla Public License, version 2.0. The full text of the
+# Mozilla Public License is at https://www.mozilla.org/MPL/2.0/
+#
+#
+# Simple RVI service caller
+#
+
+import sys
+from rvilib import RVI
+import threading
+import time
+
+def usage():
+ print "Usage:", sys.argv[0], " RVI-node service key=val ..."
+ print " RVI-node DNS name or IP of host running RVI"
+ print " service Service to invoke in RVI."
+ print " key=val Named arguments to provide to service."
+ print
+ print "Example: ./callrvi.py http://rvi1.nginfotpdx.net:8801 \\"
+ print " jlr.com/vin/aaron/4711/test/ping \\"
+ print " arg1=val1 arg2=val2"
+
+ sys.exit(255)
+
+
+#
+# Check that we have the correct arguments
+#
+if len(sys.argv) <3:
+ usage()
+
+progname = sys.argv[0]
+rvi_node = sys.argv[1]
+service = sys.argv[2]
+args = []
+i=3
+while i < len(sys.argv):
+ print sys.argv[i]
+ [k, v] = sys.argv[i].split('=')
+ args = args + [{ k: v}]
+ i = i + 1
+
+
+
+#
+# Setup an outbound JSON-RPC connection to the backend RVI node
+# Service Edge.
+#
+rvi = RVI(rvi_node)
+
+
+print "RVI Node: ", rvi_node
+print "Service: ", service
+print "args: ", args
+
+rvi.message(service, args)
+
+
+
+
+
diff --git a/python/rvi_service.py b/python/rvi_service.py
new file mode 100755
index 0000000..3c08417
--- /dev/null
+++ b/python/rvi_service.py
@@ -0,0 +1,99 @@
+#!/usr/bin/python
+
+#
+# Copyright (C) 2015, Jaguar Land Rover
+#
+# This program is licensed under the terms and conditions of the
+# Mozilla Public License, version 2.0. The full text of the
+# Mozilla Public License is at https://www.mozilla.org/MPL/2.0/
+#
+#
+# Register a service specified by command line with an RVI node.
+# Print out a message when the service gets invoked.
+#
+import sys
+from rvilib import RVI
+
+def usage():
+ print "Usage:", sys.argv[0], "<rvi_url> <service_name>"
+ print " <rvi_url> URL of Service Edge on a local RVI node"
+ print " <service_name> URL of Service to register"
+ print
+ print "The RVI Service Edge URL can be found in"
+ print "[backend,vehicle].config as"
+ print "env -> rvi -> components -> service_edge -> url"
+ print
+ print "The Service Edge URL is also logged as a notice when the"
+ print "RVI node is started."
+ print
+ print "Example: ./rvi_service.py http://rvi1.nginfotpdx.net:8801 /test/some_service"
+ sys.exit(255)
+
+
+#
+# Our general handler, registered with rvi.register_service() below.
+#
+# You can also explicitly name the arguments, but then
+# the sender has to match the argument names.
+
+# For example:
+# rvi_call.py http://localhost:8801 jlr.com/vin/test a=1 b=2 c=3 ->
+# def service(a,b,c)
+#
+def service_invoked(**args):
+ print
+ print "Service invoked!"
+ print "args:", args
+ print
+ sys.stdout.write("Press enter to quit: ")
+ sys.stdout.flush()
+ return ['ok']
+
+def services_available(services):
+ print
+ print "Services available: ", services
+ sys.stdout.write("Press enter to quit: ")
+ sys.stdout.flush()
+
+ return ['ok']
+
+def services_unavailable(services):
+ print
+ print "Services not available: ", services
+
+ sys.stdout.write("Press enter to quit: ")
+ sys.stdout.flush()
+ return ['ok']
+
+if len(sys.argv) != 3:
+ usage()
+
+# Grab the URL to use
+[ progname, rvi_node_url, service_name ] = sys.argv
+
+
+# Setup a connection to the local RVI node
+rvi = RVI(rvi_node_url)
+
+# Starting the thread that handles incoming calls is
+# not really necessary since register_service will do it for us.
+
+rvi.start_serve_thread()
+
+# Register our service and invoke 'service_invoked' if we
+# get an incoming JSON-RPC call to it from the RVI node
+#
+full_service_name = rvi.register_service(service_name, service_invoked)
+
+# Tie callbacks to be invoked as the RVI node reports to us
+# about other services being available.
+rvi.set_services_available_callback(services_available)
+rvi.set_services_unavailable_callback(services_unavailable)
+
+print "RVI General Service."
+print "RVI node URL: ", rvi_node_url
+print "Service: ", full_service_name
+
+raw_input('Press enter to quit: ')
+rvi.shutdown()
+sys.exit(0)
diff --git a/python/rvilib.py b/python/rvilib.py
new file mode 100644
index 0000000..700e032
--- /dev/null
+++ b/python/rvilib.py
@@ -0,0 +1,144 @@
+#
+# Copyright (C) 2014, Jaguar Land Rover
+#
+# This program is licensed under the terms and conditions of the
+# Mozilla Public License, version 2.0. The full text of the
+# Mozilla Public License is at https://www.mozilla.org/MPL/2.0/
+#
+# rbilib.py 0.3.0
+#
+# This moduke
+from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer
+from jsonrpclib import Server
+import random
+import time
+import threading
+
+class RVI(SimpleJSONRPCServer):
+ # address is either localhost or the ip address of self.
+ # 0.0.0.0 should work to listen to all addresses, but haven't been tested
+ # Port is either a fixed port (integer) or a tuple with a start and stop
+ # port that should be used as a random interval to pick the port from
+ def __init__(self, rvi_node_url, address='localhost', port=(20001,59999)):
+ if isinstance(port, tuple) == True:
+ (start_port, stop_port) = port
+ self.rvi_port = random.randint(start_port, stop_port)
+ else:
+ self.rvi_port = port
+
+ self.rvi_address = address
+ print self.rvi_address
+ print self.rvi_port
+ SimpleJSONRPCServer.__init__(self,addr=((self.rvi_address, self.rvi_port)), logRequests=False)
+ self.rvi_client = Server(rvi_node_url)
+ self.serve_thread = False
+
+ # Set the callback to invoke when RVI reports that one or more new
+ # services are available for invocation somewhere in the network.
+ # Arguments will be an array of fully qualified service available.
+ def set_services_available_callback(self, function):
+ self.register_function(function, 'services_available')
+
+ # Set the callback to invoke when RVI reports that one or more previously
+ # available services are no longer available for invocation
+ #
+ # Arguments will be an array of fully qualified service names no longer available.
+ def set_services_unavailable_callback(self, function):
+ self.register_function(function, 'services_unavailable')
+
+ # Register a service in this python program that should
+ # be reached from the rest of the network.
+ #
+ # service_name is the local service name ('/my_stuff/set_fan_speed')
+ #
+ # function is a python function reference to invoke when an
+ # JSON-RPC call for service_nameis received from RVI.
+ #
+ # This function will return the fully qualified name of the registered service.
+ #
+ # Thus, if you call:
+ #
+ # register_service('/my_stuff/set_fan_speed', set_fan_speed)
+ #
+ # you will get back the fully qualified service name assigned:
+ #
+ # "jlr.com/vin/YV1TS32G54F123456/my_stuff/set_fan_speed"
+ #
+ # This is the globally accessible name for the service that can be used anywhere
+ # in an RVI network to access your service.
+ #
+ def register_service(self, service_name, function):
+ # Register service_name within SimpleJSONRPCServer so that
+ # when it gets invoked with the given URL suffic, it will call 'function'.
+ #
+ # I.e. when self.url() + service_name gets called by the RVI node, we
+ # will dispatch that call to 'function'.
+ #
+ print "Will register {} to function {}".format(service_name, function)
+ self.register_function(function, service_name)
+
+ # Register the service name with the RVI node so that we get called
+ # at the URL we are listening on (self.url()).
+ #
+ res = self.rvi_client.register_service(service=service_name, network_address=self.url())
+
+ # Check if we have a thread going
+ if self.serve_thread == False:
+ print "No thread started, will do so now"
+ self.start_serve_thread()
+
+
+ # Return the fully qualified service name
+ return res['service']
+
+ def start_serve_thread(self):
+ self.serve_thread = threading.Thread(target=self.serve_forever)
+ self.serve_thread.start()
+
+ #
+ # Return the URL that we've setup for incoming JSON-RPC calls
+ # from the RVI node.
+ #
+ def url(self):
+ return 'http://' + self.rvi_address + ':' + str(self.rvi_port)
+
+
+ #
+ # Send a message to the RVI node to be forwarded to the
+ # service that registered 'service_name' in the network.
+ #
+ def message(self, service_name, parameters, timeout = int(time.time()) + 60 ):
+ print "message({}, {}, {})".format(service_name, parameters, timeout)
+ self.rvi_client.message(calling_service= "not_used",
+ service_name = service_name,
+ timeout = timeout,
+ parameters = parameters)
+
+ #
+ # Check if method is 'message', if so dispatch on
+ # name 'service_name' instead.
+ #
+ def _dispatch(self, method, params):
+ if method == 'message':
+ print "Will dispatch message to: " + params['service_name']
+ dict_param = {}
+ # Extract the 'parameters' element from the top level JSON-RPC
+ # 'param'.
+ # Convert 'parameters' from [{'vin': 1234}, {hello: 'world'}] to
+ # a regular dictionary: {'vin': 1234, hello: 'world'}
+
+ # print "Parameters:", params['parameters']
+ msg_params = params['parameters']
+ for i in range(0, len(msg_params)):
+ for j in range(0, len(msg_params[i].keys())):
+ print "params", msg_params[i].keys()[j], "=", msg_params[i].values()[j]
+ dict_param[msg_params[i].keys()[j]] = msg_params[i].values()[j]
+
+ # print "Parameter disctionary: ", dict_param
+ # print
+ # Ship the processed dispatch info upward.
+ return SimpleJSONRPCServer._dispatch(self, params['service_name'], dict_param)
+
+ # Fallthrough to all other methods.
+ # Will handle service_re3
+ return SimpleJSONRPCServer._dispatch(self,method, params)