summaryrefslogtreecommitdiff
path: root/python/rvilib.py
blob: b284c82bc31c766bc847eeab03930dd9dfab31ac (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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#
# 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.5.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
        SimpleJSONRPCServer.__init__(self,addr=((self.rvi_address, self.rvi_port)), logRequests=False)
        self.rvi_client = Server(rvi_node_url)
        self.serve_thread = False
        self.registered_services = dict()
        
    # 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')
        
    def get_available_services(self):
        # We need at least one dummy argument for the RPC to go
        # through on the RVI side.
        res = self.rvi_client.get_available_services(1)
        return res['services']

    # 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):
        # Add a prefixing slash if necessary
        if service_name[0] != '/':
            service_name = '/' + service_name

        # 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'.
        #
        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:
            self.start_serve_thread()
            
        full_service_name = res['service']
        self.registered_services[service_name] = full_service_name

        # Return the fully qualified service name
        return res['service']

    # Unregister a service 
    #  Unregisters a service previously registered with register_service
    #  The provided 'service_name' is identical to that provided tgo
    #  retister_service()
    #
    def unregister_service(self, service_name):

        if service_name[0] != '/':
            service_name = '/' + service_name

        # Check that the service has been previously registered
        # If not just return
        if service_name not in self.registered_services:
            return False
            

        # Retrieve the fully qualified service name that
        # we need to unregister from rvi
        full_service_name = self.registered_services[service_name]

        # Delete dictionary entry
        del self.registered_services[service_name]

        #
        # Unregister the service from RVI
        #
        res = self.rvi_client.unregister_service(service=full_service_name)
        
        return True

    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 ):
        self.rvi_client.message(service_name = service_name,
                                timeout = timeout,
                                parameters = parameters)
        

    #
    # Redefined shutdown method that first unregisters all services.
    #
    def shutdown(self):
        shutdown_list = self.registered_services.keys()
        for svc in shutdown_list:
            self.unregister_service(svc)

        SimpleJSONRPCServer.shutdown(self)

    #
    # 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']

            # print "Parameter disctionary: ", dict_param
            # print 
            # Ship the processed dispatch info upward.
            return SimpleJSONRPCServer._dispatch(self, params['service_name'], params['parameters'])

        # Fallthrough to all other methods.
        # Will handle service_re3
        return SimpleJSONRPCServer._dispatch(self,method, params)