Example #1
0
class RPCService(object):
    def __init__(self, function_lists, is_id_valid):
        self._rpc_dispatcher = RPCDispatcher()
        self._rpc_protocol = JSONRPCProtocol()
        self._is_id_valid = is_id_valid

        for f in function_lists:
            self._rpc_dispatcher.add_method(f)

        logger.info("RPC service initialized")

    def handle_rpccmd_message(self, context, dest_id, src_id, msg_type,
                              payload):
        # logger.debug("got RPCCMD message from src_id %s:\n%s" % (MPTN.ID_TO_STRING(src_id), MPTN.formatted_print([payload])))
        if not self._is_id_valid(dest_id):
            logger.error("invalid RPCCMD dest ID %X: not found in network" %
                         dest_id)
            return None

        if src_id != MPTN.MASTER_ID:
            logger.error("invalid RPCCMD src ID %X: should be master" % src_id)
            return None

        if payload is None:
            logger.error("packet RPCCMD should have the payload")
            return None

        try:
            request = self._rpc_protocol.parse_request(payload)
        except RPCError as e:
            response = e.error_respond()
        else:
            response = self._rpc_dispatcher.dispatch(request)

        if response is None:
            response = str(None)
        else:
            response = response.serialize()

        message = MPTN.create_packet_to_str(src_id, dest_id,
                                            MPTN.MPTN_MSGTYPE_RPCREP, response)
        MPTN.socket_send(context, src_id, message)
        return
Example #2
0
class MQTTRPC(MQTTClient):
    client_uid = CLIENT_UID
    cleansession = True
    mqtt_reply_timeout = MQTT_REPLY_TIMEOUT
    mqtt_url = MQTT_URL
    request_count = 0
    rpc_replies = {}
    replied = Event(
    )  # This event is triggered every time a new reply has come.
    subscriptions = [
    ]  # We hold a list of our subscriptions not to subscribe to

    # every request to the same client.

    def __init__(self, mqtt_url=None, client_uid=None, loop=None, config=None):
        if not loop:
            loop = asyncio.get_event_loop()
        self.loop = loop
        self.protocol = JSONRPCProtocol()
        self.dispatcher = dispatcher
        self.config = config
        if mqtt_url:
            self.mqtt_url = mqtt_url
        if client_uid:
            self.client_uid = client_uid
        super(MQTTRPC, self).__init__(client_id=self.client_uid,
                                      loop=loop,
                                      config=self.config)
        for signame in ('SIGINT', 'SIGTERM'):
            self.loop.add_signal_handler(
                getattr(signal, signame),
                lambda: asyncio.ensure_future(self.stop()))

        logger.info('Client {} initialized'.format(self.client_uid))

    async def stop(self):
        logger.info('Stopping mqttrpc...')
        # Check subscriptions
        if self._connected_state.is_set():
            await self.unsubscribe(self.subscriptions)
            await self.disconnect()
        tasks = [
            task for task in asyncio.Task.all_tasks()
            if task is not asyncio.tasks.Task.current_task()
        ]
        list(map(lambda task: task.cancel(), tasks))
        results = await asyncio.gather(*tasks, return_exceptions=True)
        logger.debug('Finished cancelling tasks, result: {}'.format(results))
        self.loop.stop()

    async def process_messages(self):
        self.mqtt_url = self.config.get('broker', {}).get('uri', self.mqtt_url)
        logger.info('Connecting to {}'.format(self.mqtt_url))
        await self.connect(self.mqtt_url, cleansession=self.cleansession)
        logger.info('Connected.')
        await self.subscribe([
            ('rpc/{}/+'.format(self.client_uid), QOS_2),
        ])
        logger.debug('Starting process messages.')
        while True:
            try:
                self.loop.create_task(
                    self.process_message(await self.deliver_message()))
            except asyncio.CancelledError:
                return

    async def process_message(self, message):
        logger.debug('Message at topic {}'.format(message.topic))

        if re.search('^rpc/(\w+)/(\w+)$', message.topic):
            # RPC request
            logger.debug('RPC request at {}'.format(message.topic))
            _, _, context = message.topic.split('/')
            data_str = message.data.decode()
            await self.receive_rpc_request(context, data_str)

        elif re.search('^rpc/(\w+)/(\w+)/reply$', message.topic):
            # RPC reply
            logger.debug('RPC reply at {}'.format(message.topic))
            _, _, context, _ = message.topic.split('/')
            data_str = message.data.decode()
            waiting_replies = self.rpc_replies.get(message.topic)
            if not waiting_replies:
                logger.warning(
                    'Got an unexpected RPC reply from {}: {}'.format(
                        message.topic, data_str))
            else:
                try:
                    data_js = json.loads(data_str)
                except json.decoder.JSONDecodeError:
                    logger.error(
                        'RPC reply bad json data: {}'.format(data_str))
                else:
                    request_id = data_js.get('id')
                    if request_id not in waiting_replies.keys():
                        logger.warning(
                            'Got a reply from {} to bad request id: {}'.format(
                                message.topic, data_str))
                    else:
                        # Finally matched the request by id
                        logger.debug(
                            'Waiting reply found for request {}'.format(
                                request_id))
                        await waiting_replies[request_id].put(data_str)
        else:
            logger.debug('Passing to on_message handler')
            await self.on_message(message)

    async def on_message(self, message):
        # Override it to implement other handlres.
        logger.debug('Not implemented')

    async def receive_rpc_request(self, context, data):
        logger.debug('Request: {}'.format(data))
        self.request_count += 1
        if type(data) != str:
            data = json.dumps(data)

        message = data

        async def handle_message(context, message):
            try:
                request = self.protocol.parse_request(message)
            except RPCError as e:
                response = e.error_respond()
            else:
                # Hack to add first params as self
                if not self in request.args:
                    request.args.insert(0, self)
                response = await self.dispatcher.dispatch(
                    request, getattr(self.protocol, '_caller', None))

            # send reply
            if response is not None:
                result = response.serialize()
                logger.debug('RPC reply to {}: {}'.format(context, result))
                self.loop.create_task(
                    self.publish(
                        'rpc/{}/{}/reply'.format(self.client_uid, context),
                        result.encode()))

        await handle_message(context, message)

    def get_proxy_for(self, destination, one_way=False):
        return RPCProxy(self, destination, one_way)

    async def _send_and_handle_reply(self,
                                     destination,
                                     req,
                                     one_way,
                                     no_exception=False):
        # Convert to bytes and send to destination
        if one_way:
            # We do not need a reply it's a notification call
            await self.publish(
                'rpc/{}/{}'.format(destination, self.client_uid),
                req.serialize().encode())
            return

        # We need a reply
        reply_topic = ('rpc/{}/{}/reply'.format(destination, self.client_uid))
        self.rpc_replies.setdefault(reply_topic, {})[req.unique_id] = Queue()
        if reply_topic not in self.subscriptions:
            logger.debug(
                'Adding subscrption to reply topic {}'.format(reply_topic))
            self.subscriptions.append(reply_topic)
            await self.subscribe([(reply_topic, QOS_2)])
            logger.debug('Subscribed to reply topic {}'.format(reply_topic))
        else:
            logger.debug('Already subscribed for topic {}'.format(reply_topic))
        await self.publish('rpc/{}/{}'.format(destination, self.client_uid),
                           req.serialize().encode())
        logger.debug('Published request id {} to {}'.format(
            req.unique_id, destination))
        try:
            reply_data = await asyncio.wait_for(
                self.rpc_replies[reply_topic][req.unique_id].get(),
                self.mqtt_reply_timeout)
            self.rpc_replies[reply_topic][req.unique_id].task_done()

        except asyncio.TimeoutError:
            del self.rpc_replies[reply_topic][req.unique_id]
            raise RPCError(
                'Reply Timeout, topic {}, id {}, method {}, args {}, kwargs {}'
                .format(reply_topic, req.unique_id, req.method, req.args,
                        req.kwargs))

        else:
            # We got a reply, handle it.
            logger.debug('Got a reply for request id: {}'.format(
                req.unique_id))
            rpc_response = self.protocol.parse_reply(reply_data)
            del self.rpc_replies[reply_topic][req.unique_id]
            # Check that there is no RPC errors.
            if not no_exception and hasattr(rpc_response, 'error'):
                raise RPCError('Error calling remote procedure: %s' %\
                               rpc_response.error)
            return rpc_response

    async def _call(self, destination, method, args, kwargs, one_way=False):
        req = self.protocol.create_request(method, args, kwargs, one_way)
        rep = await self._send_and_handle_reply(destination, req, one_way)
        if one_way:
            return
        return rep.result
def data_pesawat():
    pass

methods = {"flight-event-subscribe"     :  event_pesawat_subscribe,
           "fligt-data"                 :  data_pesawat
           }
#ini diibaratkan RPC yang masuk dari client
data_dari_client = '{"jsonrpc": "2.0", "method": "flight-event-subscribe", "id": 4}'
data_dari_client_berparam = '{"params": {"id": 1}, "jsonrpc": "2.0", "method": "authenticate", "id": 1}'
"""
setelah client mengirim rpc, maka server menangkap rpc, karna client mengirim dalam bentuk string, 
maka kita kembalikan kedalam bentuk objek rpc, jadi
1. buat instance dari JSONRPCProtocol
2. ubah string menjadi objek rpc menggunakan method parse_request 
3. buat handler
4. panggil handler
5. buat variabel untuk menampung jawaban menggunakan respond
6. dan hasil akhir dalam bentuk string menggunakan serialize siap dikirim kembali
"""
ini_adalah_instance = JSONRPCProtocol() 
request = ini_adalah_instance.parse_request(data_dari_client)
ini_adalah_handler = methods[request.method]
ini_adalah_handler()
ini_respond = request.respond("Ini jawaban") #seharusnya disimpan di fungsi pemanggil cuman karna ini mah contoh biarkan saja lah
print ini_respond.serialize()

#Untuk mengakses data berparam
request_berparam = ini_adalah_instance.parse_request(data_dari_client_berparam)
print "request_berparam.method = ", request_berparam.method
print "request_berparam.kwargs['id'] = ", request_berparam.kwargs['id']