summaryrefslogtreecommitdiff
path: root/python/rvilib.py
blob: 700e0325d98a6b19b1814f5663338cb23766f702 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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)