def __init__(self, hostAddr, hostPort, sourcePort): '''Initializes on destination host and source port. Also uses sourcePort + 1 for the server to receive commands. ''' self._hostTuple = (hostAddr, hostPort) self._client = CoapClient(sourcePort=sourcePort, dest=self._hostTuple) self._client.registerForResponse(self._responseClient) self._server = CoapServer(port=sourcePort + 1) self._server.registerForResourcePost(self._postServerResource) self._registeredPaths = {} self._notificationAction = None
def __init__(self, hostAddr, hostPort, sourcePort, query): '''Initializes on destination host and source port.''' self._hostTuple = (hostAddr, hostPort) self._client = CoapClient(sourcePort=sourcePort, dest=self._hostTuple) self._client.registerForResponse(self._responseClient) self._server = CoapServer(port=5681) self._server.registerForResourcePost(self._postServerResource) self._queryName = query
class StatsReader(object): """Reads statistics from a RIOT gcoap URL. NB: As shown in the Usage section below, StatsReader starts the asyncore loop before sending the query, like a server. This approach means the query must be called via another thread. Another approach would be to use asyncore's capabilities to create a single-use CoapClient and send the query at startup, in the same thread. Attributes: :_addrTuple: tuple IPv6 address tuple for message destination :_client: CoapClient Provides CoAP message protocol Usage: #. sr = StatsReader(sourcePort, hostAddr) -- Create instance #. sr.start() -- Starts asyncore networking loop #. sr.query() -- Runs a named query. Must be called from a different thread, for example from a timer. #. sr.close() -- Cleanup """ def __init__(self, hostAddr, hostPort, sourcePort): """Initializes on destination host and source port.""" self._hostTuple = (hostAddr, hostPort) self._client = CoapClient(sourcePort=sourcePort, dest=self._hostTuple) def start(self): """Starts networking; returns when networking is stopped.""" self._client.start() def query(self, queryName): """Runs a named query""" # create message msg = CoapMessage(self._hostTuple) msg.messageType = MessageType.NON msg.codeClass = CodeClass.Request msg.codeDetail = RequestCode.GET msg.messageId = random.randint(0, 65535) if queryName == "core": msg.addOption(CoapOption(OptionType.UriPath, ".well-known")) msg.addOption(CoapOption(OptionType.UriPath, "core")) # send message log.debug("Sending query") self._client.send(msg) def close(self): """Releases resources""" self._client.close()
class GcoapObserver(object): '''Reads statistics from a RIOT gcoap URL. Attributes: :_hostuple: tuple IPv6 address tuple for message destination :_client: CoapClient Provides CoAP client for server queries :_registeredPaths: string:bytearray, where the key is the short name for the path, and the value is the token used to register for Observe notifications for the path :_server: CoapServer Provides CoAP server for remote client commands :_notificationAction: If None, sends a normal 'ACK' response for a confirmable notification. If 'reset', sends a 'RST' response, which directs the server to deregister the client from further notifications. If 'ignore', does not send a response, which also directs the server to deregister the client for a confirmable notification. Note: 'reset_non' is NOT supported. Usage: #. sr = StatsReader(hostAddr, hostPort, sourcePort, query) -- Create instance #. sr.start() -- Starts asyncore networking loop #. sr.close() -- Cleanup ''' def __init__(self, hostAddr, hostPort, sourcePort): '''Initializes on destination host and source port. Also uses sourcePort + 1 for the server to receive commands. ''' self._hostTuple = (hostAddr, hostPort) self._client = CoapClient(sourcePort=sourcePort, dest=self._hostTuple) self._client.registerForResponse(self._responseClient) self._server = CoapServer(port=sourcePort + 1) self._server.registerForResourcePost(self._postServerResource) self._registeredPaths = {} self._notificationAction = None def _responseClient(self, message): '''Reads a response to a request ''' log.debug('Running client response handler') prefix = '0' if message.codeDetail < 10 else '' obsList = message.findOption(OptionType.Observe) obsValue = '<none>' if len(obsList) == 0 else obsList[0].value obsText = 'len: {0}; val: {1}'.format(len(obsList), obsValue) print('Response code: {0}.{1}{2}; Observe {3}'.format( message.codeClass, prefix, message.codeDetail, obsText)) if message.token in self._registeredPaths.values(): if message.messageType == MessageType.CON: if self._notificationAction == 'reset': self._sendNotifResponse(message, 'reset') elif self._notificationAction == None: self._sendNotifResponse(message, 'ack') else: # no response when _notificationAction is 'ignore' pass elif message.messageType == MessageType.NON: if self._notificationAction == 'reset_non': self._sendNotifResponse(message, 'reset') def _postServerResource(self, resource): '''Reads a command ''' log.debug('Resource path is {0}'.format(resource.path)) observeAction = None observePath = None if resource.path == '/reg/stats': observeAction = 'reg' observePath = 'stats' elif resource.path == '/reg/core': observeAction = 'reg' observePath = 'core' elif resource.path == '/reg/stats2': observeAction = 'reg' observePath = 'stats2' elif resource.path == '/dereg/stats': observeAction = 'dereg' observePath = 'stats' elif resource.path == '/dereg/core': observeAction = 'dereg' observePath = 'core' elif resource.path == '/dereg/stats2': observeAction = 'dereg' observePath = 'stats2' elif resource.path == '/notif/con_ignore': self._notificationAction = 'ignore' elif resource.path == '/notif/con_reset': self._notificationAction = 'reset' elif resource.path == '/notif/non_reset': self._notificationAction = 'reset_non' elif resource.path == '/ping': print('Got ping post') if observePath: if observeAction == 'reg' and resource.pathQuery: self._query(observeAction, observePath, tokenText=resource.pathQuery) else: self._query(observeAction, observePath) def _query(self, observeAction, observePath, tokenText=None): '''Runs the reader's query. Uses a randomly generated two byte token, or the provided string encoded bytes. :param observeAction: string -- reg (register), dereg (deregister); triggers inclusion of Observe option :param observePath: string Path for register/deregister :param tokenText: string String encoding of token bytes; must by an even-numbered length of characters like '05' or '05a6' ''' # create message msg = CoapMessage(self._hostTuple) msg.messageType = MessageType.NON msg.codeClass = CodeClass.Request msg.codeDetail = RequestCode.GET msg.messageId = random.randint(0, 65535) if observePath == 'core': msg.addOption(CoapOption(OptionType.UriPath, '.well-known')) msg.addOption(CoapOption(OptionType.UriPath, 'core')) elif observePath == 'stats': msg.addOption(CoapOption(OptionType.UriPath, 'cli')) msg.addOption(CoapOption(OptionType.UriPath, 'stats')) elif observePath == 'stats2': msg.addOption(CoapOption(OptionType.UriPath, 'cli')) msg.addOption(CoapOption(OptionType.UriPath, 'stats2')) if observeAction == 'reg': # register msg.addOption(CoapOption(OptionType.Observe, 0)) if tokenText: msg.tokenLength = len(tokenText) / 2 msg.token = bytearray(msg.tokenLength) for i in range(0, msg.tokenLength): msg.token[i] = int(tokenText[2 * i:2 * (i + 1)], base=16) else: msg.tokenLength = 2 msg.token = bytearray(2) msg.token[0] = random.randint(0, 255) msg.token[1] = random.randint(0, 255) self._registeredPaths[observePath] = msg.token elif observeAction == 'dereg': # deregister msg.addOption(CoapOption(OptionType.Observe, 1)) msg.tokenLength = 2 msg.token = self._registeredPaths[observePath] # assume deregistration will succeed del self._registeredPaths[observePath] # send message log.debug('Sending query') self._client.send(msg) def _sendNotifResponse(self, notif, responseType): '''Sends an empty ACK or RST response to a notification :param notif: CoapMessage Observe notification from server ''' msg = CoapMessage(notif.address) msg.codeClass = CodeClass.Empty msg.codeDetail = ClientResponseCode.Empty msg.messageId = notif.messageId msg.tokenLength = 0 msg.token = None if responseType == 'reset': msg.messageType = MessageType.RST else: msg.messageType = MessageType.ACK log.debug('Sending {0} for notification response'.format(responseType)) self._client.send(msg) def start(self): '''Starts networking; returns when networking is stopped. Only need to start client, which automatically starts server, too. ''' self._client.start() def close(self): '''Releases resources''' self._client.close()
def __init__(self, hostAddr, hostPort, sourcePort): """Initializes on destination host and source port.""" self._hostTuple = (hostAddr, hostPort) self._client = CoapClient(sourcePort=sourcePort, dest=self._hostTuple)
class StatsReader(object): '''Reads statistics from a RIOT gcoap URL. NB: As shown in the Usage section below, StatsReader starts the asyncore loop before sending the query, like a server. This approach means the query must be called via another thread. Another approach would be to use asyncore's capabilities to create a single-use CoapClient and send the query at startup, in the same thread. Attributes: :_addrTuple: tuple IPv6 address tuple for message destination :_client: CoapClient Provides CoAP message protocol :_queryName: string GET query to send to CoAP server Usage: #. sr = StatsReader(hostAddr, hostPort, sourcePort, query) -- Create instance #. sr.start() -- Starts asyncore networking loop #. sr.close() -- Cleanup ''' def __init__(self, hostAddr, hostPort, sourcePort, query): '''Initializes on destination host and source port.''' self._hostTuple = (hostAddr, hostPort) self._client = CoapClient(sourcePort=sourcePort, dest=self._hostTuple) self._client.registerForResponse(self._responseClient) self._server = CoapServer(port=5681) self._server.registerForResourcePost(self._postServerResource) self._queryName = query def _responseClient(self, message): '''Reads a response to a request ''' prefix = '0' if message.codeDetail < 10 else '' obsList = message.findOption(OptionType.Observe) obsValue = '<none>' if len(obsList) == 0 else obsList[0].value obsText = 'len: {0}; val: {1}'.format(len(obsList), obsValue) print('Response code: {0}.{1}{2}; Observe {3}'.format(message.codeClass, prefix, message.codeDetail, obsText)) def _postServerResource(self, resource): '''Reads a command ''' log.debug('Resource path is {0}'.format(resource.path)) observeAction = None if resource.path == '/reg': observeAction = 'reg' elif resource.path == '/dereg': observeAction = 'dereg' self._query(observeAction) def _query(self, observeAction): '''Runs the reader's query :param observeAction: reg (register), dereg (deregister), or None; triggers inclusion of Observe option ''' # create message msg = CoapMessage(self._hostTuple) msg.messageType = MessageType.NON msg.codeClass = CodeClass.Request msg.codeDetail = RequestCode.GET msg.messageId = random.randint(0, 65535) msg.tokenLength = 2 msg.token = (0x35, 0x61) if self._queryName == 'core': msg.addOption( CoapOption(OptionType.UriPath, '.well-known') ) msg.addOption( CoapOption(OptionType.UriPath, 'core') ) elif self._queryName == 'stats': msg.addOption( CoapOption(OptionType.UriPath, 'cli') ) msg.addOption( CoapOption(OptionType.UriPath, 'stats') ) if observeAction == 'reg': # register msg.addOption( CoapOption(OptionType.Observe, 0) ) elif observeAction == 'dereg': # deregister msg.addOption( CoapOption(OptionType.Observe, 1) ) # send message log.debug('Sending query') self._client.send(msg) def start(self): '''Starts networking; returns when networking is stopped. Only need to start client, which automatically starts server, too. ''' self._client.start() def close(self): '''Releases resources''' self._client.close()