summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO6
-rw-r--r--curl_scripts/device_message.sh5
-rw-r--r--hvac_demo/README.md11
-rwxr-xr-xhvac_demo/hvac_emulator.py237
-rwxr-xr-xhvac_demo/hvac_subscription_service.py99
-rw-r--r--hvac_demo/rvi_json_rpc_server.py33
-rw-r--r--priv/setup_device.config3
7 files changed, 391 insertions, 3 deletions
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..614d6c3
--- /dev/null
+++ b/TODO
@@ -0,0 +1,6 @@
+In doc and code, convert JSON-RPC param "parameters" to "arguments" in order to avoid
+ confusion with JSON-RPC "params"
+
+In doc and code, rename "target" "service" or potentially "target service"
+
+
diff --git a/curl_scripts/device_message.sh b/curl_scripts/device_message.sh
index 81c144c..6a28cdd 100644
--- a/curl_scripts/device_message.sh
+++ b/curl_scripts/device_message.sh
@@ -20,10 +20,11 @@ curl -u $USER_AUTH -k -X POST $URL -d @- << EOF
"params":
{
"calling_service": "hvac_app",
- "target": "jlr.com/backend/hvac/publish_fan_speed",
+ "target": "jlr.com/backend/hvac/subscribe",
"timeout": 1405099531,
"parameters": [
- { "speed": 5 }
+ { "vin": 1234 },
+ { "subscribing_service": "jlr.com/vin/1234/hvac/updates" }
]
}
}
diff --git a/hvac_demo/README.md b/hvac_demo/README.md
new file mode 100644
index 0000000..01821d2
--- /dev/null
+++ b/hvac_demo/README.md
@@ -0,0 +1,11 @@
+The HVAC service is a test service that registers with the backend server to handle a very simple pub/sub setup.
+
+Accepted commands are:
+
+subscribe(vin, subscriber) -> Any updates to the given vin should be sent as publish command to the given subscriber (a service running on a device).
+unsubscribe(vin, subscriber) -> Remove subscription conenction between subscriber and vin.
+
+publish(vin, key, value) -> A given vin is updating a key with a new value. All subscribers to the vin will be notified.
+
+Dependencies:
+pip install jsonrpclib
diff --git a/hvac_demo/hvac_emulator.py b/hvac_demo/hvac_emulator.py
new file mode 100755
index 0000000..a9cc863
--- /dev/null
+++ b/hvac_demo/hvac_emulator.py
@@ -0,0 +1,237 @@
+#!/usr/bin/python
+
+#
+# Emulate a mobile device or an IVI.
+#
+# This emulator connects to an RVI Service Edge as a service and
+# takes on one of two roles:
+#
+# 1) mobile device
+# In this mode the emulator connects to the Service Edge of the
+# backend server (since P2P will be supported in RVI Project
+# Milestone 2), and subscribes to HVAC updates (temp, fan speed,
+# etc) updates from an IVI with a given VIN. The emulator can also
+# update HVAC values, simulating HVAC GUI input on the phone
+# screen, and send it off to the (backend server) Service Edge for
+# further distribution to the targeted IVI.
+#
+# 2) IVI
+# In this mode, the emulator connects to the Service Edge of a
+# vehicle (device) and subscribes to updates from one or more
+# mobile devices. Local updates, simulating GUI input on the
+# HVAC screen, can be entered at the command line and will be
+# distributed to the phone(s) subscribing to updates from
+# the IVI.
+#
+#
+# In both modes, the emulator sends its updated HVAC values to the
+# service jlr.com/backend/hvac/subscription_service/publish. This
+# service will look up VIN targeted by the update and send out the
+# same data to all services that are subscribing to that VIN.
+#
+# In mobile device mode, the emulator will subscribe to updated
+# values published to to the VIN value "ivi_[vin]" where vin is the
+# VIN number of the IVI. When a emulator, still in mobile device
+# mode, sends out an updated value (entered in the python console),
+# it will publish the value using the vin "mobile_[vin]"
+#
+# Converesely, an emulator in IVI mode will sbuscribe to
+# "mobile_[vin]", and publish to "ivi_[vin]".
+#
+# This setup allows the mobile device emulator to receive updates
+# entered on the IVI HVAC screen (sent out by the IVI to
+# "ivi_[vin]"), while the IVI emulator receives updates entered on
+# the mobile device screen (sent out by the mobile device to
+# "mobile_[vin]".
+#
+# When the emulator connects in IVI mode to a device RVI node, the
+# node's configured service prefix (see priv/setup_device.config,
+# node_service_prefix) will have the VIN number as a part of the
+# prefix. The emulator will thus register its service as /hvac/publish,
+# which, prefixed with the node service prefix, gives it a complete name
+# of jlr.com/vin/[vin]/hvac/publish.
+#
+# In mobile device mode, the emulator connects to the backend RVI node, which
+# has a service prefix of jlr.com/backend. In this mode, the emulator
+# will register with a phone number as a part of its service name, where
+# the phone number is specified as a command line argument to the
+# emulator.
+#
+# Since the backend RVI node has a service prefix of jlr.com/backend
+# (see priv/setup_backend.config), the mobile device emulator will
+# have a complete service name of:
+# jlr.com/backend/mobile/[phone_number]/hvac/publish
+#
+# [phone_number] is given at the command line.
+#
+#
+# Usage:
+#
+# python hvac_emulator.py <rvi_url> [mobile <phone_number> | ivi]
+#
+# mob
+# The rvi_url is the local URL of the RVI Service Edge.
+# See
+#
+# Example:
+# python hvac_emulator.pyh mobile 9491231234 http://127.0.0.1:8811
+#
+# python hvac_emulator.pyh ivi saw222992212 http://127.0.0.1:8800
+#
+import sys
+from rvi_json_rpc_server import RVIJSONRPCServer
+import jsonrpclib
+import random
+import threading
+
+# The subscriber service (MQTT server equivalent), that
+# manages subscriptions and distributes received publish commands to
+# the relevant subscribers.
+#
+SUBSCRIPTION_SERVICE_BASE='jlr.com/backend/subscription_service'
+SUBSCRIBE_SERVICE=SUBSCRIPTION_SERVICE_BASE+'/subscribe'
+UNSUBSCRIBE_SERVICE=SUBSCRIPTION_SERVICE_BASE+'/unsubscribe'
+PUBLISH_SERVICE=SUBSCRIPTION_SERVICE_BASE+'/publish'
+
+#
+# Publish command is invoked by the
+# subscriber server above when it receives a publish command
+# from a VIN that this emulator subscribes to.
+#
+def publish(vin, key, value):
+ print "Publish invoked!"
+ print "vin:", viny
+ print "key:", key
+ print "value:", value
+ return ['ok']
+
+def usage():
+ print "Usage:", sys.argv[0], "<rvi_url> mobile <phone_number> <vin> | ivi"
+ print " ivi Emulate an IVI."
+ print " mobile <phone_number> <vin> Emulate a mobile device with number given in"
+ print " <phone_number> number, communicating with <vin>"
+ print " <rvi_url> URL of RVI Service Edge on local host"
+ print
+ print "The RVI Service Edge URL can be found in"
+ print "priv/setup_[backend,device].config as"
+ print "env -> rvi -> components -> service_edge -> url"
+ sys.exit(255)
+
+
+# Setup self's server that we will receive incoming calls from service edge on.
+
+#
+# Setup a localhost URL, using a random port, that we will listen to
+# incoming JSON-RPC publish calls on, delivered by our RVI service
+# edge (specified by rvi_url).
+#
+emulator_service_host = 'localhost'
+emulator_service_port = random.randint(20001, 59999)
+
+
+
+
+
+#
+# Check that we have the correct arguments
+#
+if len(sys.argv) == 5:
+ [ progname, rvi_url, mode, phone_number, vin ] = sys.argv
+ if mode != 'mobile':
+ print
+ print "Second argument, when three parameters are specified, must "
+ print "be 'mobile', not", mode, "."
+ print
+ usage()
+
+ # We are in mobile device mode. Setup the vin numbers for publish
+ pub_vin = "mobile_"+vin
+ sub_vin = "ivi_"+vin
+
+ # Setup the service name we will register with.
+ # The complete service name will be: jlr.com/backend/mobile/<phone_nr>/hvac/publish
+ emulator_service_name = '/mobile/'+phone_number+'/hvac/publish'
+
+ print "Will run in mobile device mode."
+ print "Backend server node URL: ", rvi_url
+ print "Phone Number: ", phone_number
+ print "VIN: ", vin
+
+elif len(sys.argv) == 3:
+ [ progname, rvi_url, mode ] = sys.argv
+ if mode != 'ivi':
+ print
+ print "Second argument, when two are specified, must "
+ print "be 'ivi', not", mode, "."
+ print
+ usage()
+
+ # setup the service name we will register with
+ # The complete service name will be: jlr.com/vin/<vin>/hvac/publish
+ emulator_service_name = '/hvac/publish'
+
+ # Setup an outbound JSON-RPC connection to the RVI Service Edeg.
+ rvi_server = jsonrpclib.Server(rvi_url)
+
+ # Register our HVAC IVI/mobile emulator service with the RVI Service Edge,
+ # allowing the RVI to forward requests to the service name to the
+ # given network addresss (URL):
+ res = rvi_server.register_service(service = emulator_service_name,
+ network_address =
+ 'http://'+emulator_service_host + ':' + str(emulator_service_port))
+
+ # The returned full service name contains the VIN number that we want:
+ # jlr.com/vin/<vin>/hvac/publish
+ # We need to dig out the <vin> bit
+
+ print "res = ", res
+ svc_name = res['service']
+
+ [ t1, t2, vin, t3, t4] = svc_name.split('/')
+ print "vin:", vin,
+
+ # We are in mobile device mode. Setup the vin numbers for publish
+ pub_vin = "ivi_"+vin
+ sub_vin = "mobile_"+vin
+
+ print "Will run in IVI mode."
+ print "Device node URL: ", rvi_url
+ print "VIN: ", vin
+else:
+ usage()
+
+
+# Regsiter self's service with the backend server RVI node
+# See rvi_json_rpc_server.py._dispatch() for details on how
+# ncoming JSON-RPC requests are mapped to local funcitons.
+#
+emulator_service = RVIJSONRPCServer(((emulator_service_host, emulator_service_port)))
+emulator_service.register_function(publish, emulator_service_name)
+
+# Send of a subscribe to the subscription service running on the
+# backend. See hvac_subscription_service.py.subscribe() for details.
+
+rvi_server.message(calling_service = emulator_service_name,
+ target = SUBSCRIBE_SERVICE,
+ timeout = 0,
+ parameters = [{ u'vin': sub_vin,
+ u'subscribing_service': emulator_service_name}])
+
+# Create a thread to handle incoming stuff so that we can do input
+# in order to get new values
+thr = threading.Thread(target=emulator_service.serve_forever)
+thr.start()
+
+while True:
+ # Read a line and split it into a key val pair
+ [k, v] = raw_input('Enter <key> <val>: ').split(' ')
+
+ # Send out update to the subscriber
+ rvi_server.message(calling_service=emulator_service_name,
+ target = PUBLISH_SERVICE,
+ timeout = 0,
+ parameters = [{ u'vin': pub_vin,
+ u'key': k,
+ u'val': v}])
+
+ print('Key {} set to {} for vin{}'. format(k, v, vin))
diff --git a/hvac_demo/hvac_subscription_service.py b/hvac_demo/hvac_subscription_service.py
new file mode 100755
index 0000000..88c8435
--- /dev/null
+++ b/hvac_demo/hvac_subscription_service.py
@@ -0,0 +1,99 @@
+#!/usr/bin/python
+
+from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer
+import jsonrpclib
+from rvi_json_rpc_server import RVIJSONRPCServer
+
+RVI_SERVICE_EDGE='http://localhost:8801'
+HVAC_SERVER=('localhost', 8901)
+
+# vin_subs is a simple dictionary with vin numbers as keys and arrays
+# of services as values.
+# During a subscribe operation, the provided service is added
+# as a list element in value, where the list hangs under the key
+# with value 'vin'
+#
+# Thus:
+# { '1234': [ 'jlr.com/vin/5555/hvac_update_ui', 'jlr.com/vin/4711/hvac_update_ui']
+vin_subs = []
+
+def subscribe(vin, subscribing_service):
+ print "Subscribe"
+ print "vin:", vin
+ print "subscribing_service:", subscribing_service
+
+ # Delete any existing service with the same name
+ try:
+ vin_subs[vin].remove(subscribing_service)
+ except Err:
+ pass
+
+ # Add the subscribing service
+ vin_subs[vin].append(subscribing_service)
+ return ['ok']
+
+
+def unsubscribe(vin, subscribing_service):
+ print "Unsubscribe"
+ print "vin:", vin
+ print "subscribing_service:", subscribing_service
+
+ # Delete any existing service with the same name
+ try:
+ vin_subs[vin].remove(subscribing_service)
+ except Err:
+ pass
+
+ return ['ok']
+
+def publish(vin, key, value):
+ print "Publish"
+ print "vin:", vin
+ print "key:", key
+ print "value:", value
+
+ # Delete any existing service with the same name
+ try:
+ subs = vin_subs[vin]
+ except Err:
+ print "No subscribers for vin:", vin
+ return ['ok']
+
+ for sub in subs:
+ print "Sending publish to", sub
+ rvi_server.message(calling_service = '/hvac/publish',
+ target = sub,
+ timeout = 0,
+ parameters = [{ u'key': key}, {u'value': value }])
+
+
+# Setup self's server that we will receive incoming calls from service edge on.
+hvac_server = RVIJSONRPCServer(HVAC_SERVER)
+# hvac_server = SimpleJSONRPCServer(HVAC_SERVER)
+
+hvac_server.register_function(subscribe, '/hvac/subscriber_service/subscribe')
+hvac_server.register_function(unsubscribe, '/hvac/subscriber_service/unsubscribe')
+hvac_server.register_function(publish, '/hvac/subscriber_service/publish')
+
+
+# Register our services with the service edge and have
+# it associate our service name with the URL that the hvac_server is listening on
+rvi_server = jsonrpclib.Server(RVI_SERVICE_EDGE)
+
+res = rvi_server.register_service(service = '/subscription_service/subscribe',
+ network_address = 'http://localhost:8901')
+
+print "Registered service /subscription_service/subscribe as", res['service']
+
+res = rvi_server.register_service(service = '/subscription_service/unsubscribe',
+ network_address = 'http://localhost:8901')
+
+print "Registered service /subscription_service/unsubscribe as", res['service']
+
+res = rvi_server.register_service(service = '/subscription_service/publish',
+ network_address = 'http://localhost:8901')
+
+print "Registered service /subscription_service/publish as", res['service']
+
+
+hvac_server.serve_forever()
diff --git a/hvac_demo/rvi_json_rpc_server.py b/hvac_demo/rvi_json_rpc_server.py
new file mode 100644
index 0000000..7235756
--- /dev/null
+++ b/hvac_demo/rvi_json_rpc_server.py
@@ -0,0 +1,33 @@
+from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer
+import jsonrpclib
+
+class RVIJSONRPCServer(SimpleJSONRPCServer):
+ # Check if method is 'message', if so dispatch on
+ # name 'target' instead.
+ def _dispatch(self, method, params):
+ print "dispatch:", params
+ if method == 'message':
+ print "Will dispatch message to: " + params['target']
+ 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 params['parameters']
+ msg_params = params['parameters']
+ for i in range(0, len(msg_params)):
+ print "params ", msg_params[i].keys()[0], " = ", msg_params[i].values()[0]
+ dict_param[msg_params[i].keys()[0]] = msg_params[i].values()[0]
+
+ print "DICT: ", dict_param
+ # Ship the processed dispatch info upward.
+ return SimpleJSONRPCServer._dispatch(self, params['target'], dict_param)
+
+ print "Method:", method
+ for x in params:
+ print "params ", x, " = ",params[x]
+ return SimpleJSONRPCServer._dispatch(self,message, params)
+
+ print "---"
+
diff --git a/priv/setup_device.config b/priv/setup_device.config
index 3ef2770..55accd7 100644
--- a/priv/setup_device.config
+++ b/priv/setup_device.config
@@ -44,7 +44,8 @@
{rvi,
[
- { node_address, "127.0.0.1:9991" }, %% Should matcch bert_rpc_server below
+ %% Should matcch bert_rpc_server below
+ { node_address, "127.0.0.1:9991" },
{ node_service_prefix, "jlr.com/vin/1234/"},
{ backend_address, "127.0.0.1:9990" }, %% See data_link_device:send_data() for details