def post(self): """Method to create a gateway""" host = self.args['host'] name = self.args['name'] eui = self.args['eui'] enabled = self.args['enabled'] power = self.args['power'] message = {} # Check for required args required = {'host', 'name', 'eui', 'enabled', 'power'} for r in required: if self.args[r] is None: message[r] = "Missing the {} parameter.".format(r) if message: abort(400, message=message) # Ensure we have a valid address try: ipaddress.ip_address(host) except (ipaddress.AddressValueError, ValueError): message = {'error': "Invalid IP address {} ".format(host)} abort(400, message=message) # Ensure we have a valid EUI if not isinstance(eui, (int, long)): message = {'error': "Invalid gateway EUI {} ".format(eui)} abort(400, message=message) # Check this gateway does not currently exist exists = yield Gateway.exists(where=['host = ?', host]) if exists: message = {'error': "Gateway address {} ".format(host) + \ "currently exists."} abort(400, message=message) # Check the EUI does not currently exist exists = yield Gateway.exists(where=['eui = ?', eui]) if exists: message = {'error': "Gateway EUI {} ".format(eui) + \ "currently exists."} abort(400, message=message) # Create and validate gateway = Gateway(host=host, eui=eui, name=name, enabled=enabled, power=power) (valid, message) = gateway.valid() if not valid: abort(400, message=message) try: g = yield gateway.save() if g is None: abort(500, message={'error': "Error saving the gateway."}) # Add the new gateway to the server. self.server.lora.addGateway(g) location = self.restapi.api.prefix + '/gateway/' + str(host) returnValue(({}, 201, {'Location': location})) except TimeoutError: # Exception returns 500 to client log.error("REST API timeout for gateway POST request")
def publishMessage(self, data): """Publish the MQTT message. Any inbound messages are copied to the messages list attribute, and returned to the caller. Args: data (str): Application data to send Returns: A list of received messages. """ # Start the service, and add a timeout to check the connection. self.client.startService() reactor.callLater(self.TIMEOUT, self.checkConnection) # Attempt to connect. If we tiemout and cancel and exception # is thrown. try: yield self.client.whenConnected().addCallback( self.azureConnect, data) except Exception as e: log.error("Azure MQTT service failed to connect to broker.") # Stop the service if sucessful, and finally return # any inbound messages. else: yield self.client.stopService() finally: returnValue(self.messages)
def start(self, netserver): """Load all application interfaces and start them. Args: netserver (NetServer): The network server """ self.netserver = netserver # Get all concrete application interface objects appinterfaces = yield AppInterface.all() for appinterface in appinterfaces: # Get the interface, set the appinterface interface = yield appinterface.interfaces.get() if interface: interface.appinterface = appinterface self.interfaces.append(interface) # Start the interfaces for interface in self.interfaces: log.info("Starting application interface id {id}: {name}", id=interface.appinterface.id, name=interface.name) interface.start(self.netserver) if not interface.started: log.error("Could not start application interface " "id {id}", id=interface.appinterface.id)
def put(self, appeui): """Method to handle application PUT requests Args: appeui (int): Application EUI """ try: app = yield Application.find(where=['appeui = ?', appeui], limit=1) # Return a 404 if not found. if app is None: abort(404, message={ 'error': "Application {} doesn't exist.".format( euiString(appeui)) }) kwargs = {} for a, v in self.args.items(): if v is not None and v != getattr(app, a): kwargs[a] = v setattr(app, a, v) (valid, message) = yield app.valid() if not valid: abort(400, message=message) # Update the model if kwargs: app.update(**kwargs) returnValue(({}, 200)) except TimeoutError: log.error("REST API timeout retrieving application {appeui}", appeui=euiString(appeui))
def post(self): """Method to create an application""" appeui = self.args['appeui'] name = self.args['name'] domain = self.args['domain'] appnonce = self.args['appnonce'] appkey = self.args['appkey'] fport = self.args['fport'] appinterface_id = self.args['appinterface_id'] message = {} # Check for required args required = {'appeui', 'name', 'appnonce', 'appkey', 'fport'} for r in required: if self.args[r] is None: message[r] = "Missing the {} parameter.".format(r) if message: abort(400, message=message) # Check this application does not currently exist exists = yield Application.exists(where=['appeui = ?', appeui]) if exists: message = { 'error': "Application EUI {} currently exists".format(euiString(appeui)) } abort(400, message=message) # Check the appkey doesn't exist exists = yield Application.exists(where=['appkey = ?', appkey]) if exists: message = { 'error': "Application key {} currently exists".format( intHexString(appkey, 16)) } abort(400, message=message) # Create and validate app = Application(appeui=appeui, name=name, domain=domain, appnonce=appnonce, appkey=appkey, fport=fport, appinterface_id=appinterface_id) (valid, message) = yield app.valid() if not valid: abort(400, message=message) try: a = yield app.save() if a is None: abort(500, message={'error': "Error saving the application."}) location = self.restapi.api.prefix + '/app/' + str(appeui) returnValue(({}, 201, {'Location': location})) except TimeoutError: # Exception returns 500 to client log.error("REST API timeout for application POST request")
def delete(self, appinterface_id): """Method to handle AppInterface DELETE requests Args: appinterface_id (int): Application inetrface id """ try: # Check that no interfaces exist with this interface_id. interface = yield interfaceManager.getInterface(appinterface_id) if interface is None: abort(404, message={ 'error': "Interface {} doesn't exist.".format( str(appinterface_id)) }) # Delete the interface via interfaceManager yield interfaceManager.deleteInterface(interface) returnValue(({}, 200)) except TimeoutError: log.error( "REST API timeout retrieving application interface " "{id}", id=appinterface_id)
def updateInterface(self, interface): """Update an existing interface Args: interface: The concrete application interface """ # Save interface yield interface.save() interface.appinterface = yield interface.appinterfaces.get() # Retrieve the current running interface and its index (index, current) = next( ((i, iface) for i, iface in enumerate(self.interfaces) if iface.appinterface.id == interface.appinterface.id), (None, None)) # Stop and remove the current interface if current: current.stop() del self.interfaces[index] # Append the new interface and start self.interfaces.append(interface) interface.start(self.netserver) if not interface.started: log.error("Could not start application interface " "id {id}", interface.appinterface.id)
def delete(self, appeui): """Method to handle application DELETE requests Args: appeui (int): Application EUI """ try: # Check that no devices exist with this AppEUI. devices = yield Device.find(where=['appeui = ?', appeui], limit=1) if devices is not None: abort(400, message={'error': "Cannot delete - devices exist " \ "with Application EUI {}".format(euiString(appeui))}) # Return a 404 if not found. app = yield Application.find(where=['appeui = ?', appeui], limit=1) if app is None: abort(404, message={ 'error': "Application {} doesn't exist.".format( euiString(appeui)) }) yield app.delete() returnValue(({}, 200)) except TimeoutError: log.error("REST API timeout retrieving application {appeui}", appeui=euiString(appeui))
def get(self, appinterface_id): """Method to handle application GET requests Args: appinterface_id (int): Application Interface ID """ try: interface = interfaceManager.getInterface(appinterface_id) # Return a 404 if not found. if interface is None: abort(404, message={ 'error': "Application interface id {} " "doesn't exist.".format(str(appinterface_id)) }) # Return the interface's marshalled attributes returnValue(interface.marshal()) yield except TimeoutError: log.error( "REST API timeout retrieving application interface " "{id}", id=appinterface_id)
def put(self, host): """Method to handle gateway PUT requests Args: host (str): Gateway host address """ try: gateway = yield Gateway.find(where=['host = ?', host], limit=1) # Return a 404 if not found. if gateway is None: abort(404, message={'error': "Gateway {} doesn't exist".format(host)}) kwargs = {} for a,v in self.args.items(): if v is not None and v != getattr(gateway, a): kwargs[a] = v setattr(gateway, a, v) (valid, message) = yield gateway.valid() if not valid: abort(400, message=message) # Update the gateway and server with the new attributes if kwargs: gateway.update(**kwargs) self.server.lora.updateGateway(host, gateway) returnValue(({}, 200)) except TimeoutError: log.error("REST API timeout retrieving gateway {host}", host=host)
def createInterface(self, interface): """Add an interface to the interface list" Args: interface: The concrete application interface Returns: Appinterface id on success """ # Create the interface and AppInterface yield interface.save() appinterface = AppInterface() yield appinterface.save() yield interface.appinterfaces.set([appinterface]) # Add the new interface to the list interface.appinterface = appinterface self.interfaces.append(interface) # Start the interface interface.start(self.netserver) if not interface.started: log.error("Could not start application interface " "id {id}", interface.appinterface.id) returnValue(appinterface.id) yield
def azureConnect(self, protocol, data): self.connected = True protocol.setWindowSize(1) protocol.onPublish = self.onPublish pubtopic = 'devices/{}/messages/events/'.format(self.devid) subtopic = 'devices/{}/messages/devicebound/#'.format(self.devid) try: # Connect and subscribe yield protocol.connect(self.devid, username=self.username, password=self.password, cleanStart=False, keepalive=10) yield protocol.subscribe(subtopic, 2) except Exception as e: log.error( "Azure MQTT service could not connect to " "Azure IOT Hub using username {name}", name=self.username) returnValue(None) # Publish the outbound message yield protocol.publish(topic=pubtopic, qos=0, message=str(data))
def get(self, appeui): """Method to handle application property GET requests Args: appeui (int): Application EUI port (int): Application property port """ try: app = yield Application.find(where=['appeui = ?', appeui], limit=1) # Return a 404 if not found. if app is None: abort(404, message={ 'error': "Application {} doesn't exist.".format( euiString(appeui)) }) port = self.args['port'] p = yield AppProperty.find( where=['application_id = ? AND port = ?', app.id, port]) if p is None: abort(404, message={'error': "Application property doesn't exist."}) data = marshal(p, self.fields) returnValue(data) except TimeoutError: log.error( "REST API timeout get request for application {appeui} " "property {port}", appeui=euiString(appeui), port=port)
def get(self, appeui): """Method to get all app properties""" try: # Return a 404 if the application is not found. app = yield Application.find(where=['appeui = ?', appeui], limit=1) if app is None: abort(404, message={ 'error': "Application {} doesn't exist.".format( euiString(appeui)) }) # Get the properties properties = yield app.properties.get() if properties is None: returnValue({}) data = {} for i, p in enumerate(properties): data[i] = marshal(p, self.fields) returnValue(data) except TimeoutError: log.error( "REST API timeout retrieving application {appeui} " "properties", appeui=euiString(appeui))
def post(self): """Method to create an application property""" appeui = self.args['appeui'] port = self.args['port'] name = self.args['name'] type = self.args['type'] message = {} # Check for required args required = {'appeui', 'port', 'name', 'type'} for r in required: if self.args[r] is None: message[r] = "Missing the {} parameter.".format(r) if message: abort(400, message=message) # Return a 404 if the application is not found. app = yield Application.find(where=['appeui = ?', appeui], limit=1) if app is None: abort(404, message={ 'error': "Application {} doesn't exist.".format(euiString(appeui)) }) # Check this property does not currently exist exists = yield AppProperty.exists( where=['application_id = ? AND port = ?', app.id, port]) if exists: message = { 'error': "Application property for appeui {}, port " "{} exists".format(euiString(appeui), port) } abort(400, message=message) # Create and validate p = AppProperty(application_id=app.id, port=port, name=name, type=type) (valid, message) = yield p.valid() if not valid: abort(400, message=message) try: prop = yield p.save() if prop is None: abort(500, message={'error': "Error saving the property."}) location = self.restapi.api.prefix + '/property/' + str(prop.id) returnValue(({}, 201, {'Location': location})) except TimeoutError: log.error( "REST API timeout post application {appeui} " "property {port}", appeui=euiString(appeui), port=port)
def get(self, host): """Method to handle gateway GET requests""" try: g = yield Gateway.find(where=['host = ?', host], limit=1) # Return a 404 if not found. if g is None: abort(404, message={'error': "Gateway {} doesn't exist.".format(host)}) returnValue(marshal(g, self.fields)) except TimeoutError: log.error("REST API timeout retrieving gateway {host}", host=host)
def get(self): """Method to handle system configuration GET requests""" try: config = yield Config.find(limit=1) # Return a 404 if not found. if config is None: abort(404, message={'error': "Could not get the system configuration"}) returnValue(marshal(config, self.fields)) except TimeoutError: log.error("REST API timeout retrieving application {appeui}", appeui=euiString(appeui))
def get(self): """Method to get all gateways""" try: gateways = yield Gateway.all() if gateways is None: returnValue({}) data = {} for i,g in enumerate(gateways): data[i] = marshal(g, self.fields) returnValue(data) except TimeoutError: # Exception returns 500 to client log.error("REST API timeout retrieving all gateways")
def start(self): """Start the netserver. Sets up scheduled tasks and start listening on the required interfaces. """ log.info("Starting the server") # Setup scheduled tasks # 1. ADR Requests self.task['processADRRequests'] = task.LoopingCall( self._processADRRequests) if self.config.adrenable: self.task['processADRRequests'].start(self.config.adrcycletime) # 2. Message cache self.task['cleanMessageCache'] = task.LoopingCall( self._cleanMessageCache) self.task['cleanMessageCache'].start( max(10, self.config.duplicateperiod * 2)) # 3. MAC Command queue self.task['manageMACCommandQueue'] = task.LoopingCall( self._manageMACCommandQueue) if self.config.macqueueing: self.task['manageMACCommandQueue'].start( self.config.macqueuelimit / 2) # Start the web server log.info("Starting the web server") self.webserver = WebServer(self) try: self.webserver.start() except CannotListenError: log.error("Error starting the web server: cannot listen.") reactor.stop() # Start server network interfaces: # LoRa gateway interface log.info("Starting the LoRaWAN interface") self.lora = LoraWAN(self) try: self.lora.start() except CannotListenError: log.error("Error opening LoRa interface UDP port {port}", port=self.config.port) reactor.stop() # Application interfaces interfaceManager.start(self)
def get(self): """Method to get all applications""" try: apps = yield Application.all() if apps is None: returnValue({}) data = {} for i, a in enumerate(apps): data[i] = marshal(a, self.fields) data[i]['properties'] = yield self.getProperties(a) returnValue(data) except TimeoutError: log.error("REST API timeout retrieving application {appeui}", appeui=euiString(appeui))
def put(self, appeui): """Method to handle application property PUT requests Args: appeui (int): Application EUI """ try: app = yield Application.find(where=['appeui = ?', appeui], limit=1) # Return a 404 if not found. if app is None: abort(404, message={ 'error': "Application {} doesn't exist.".format( euiString(appeui)) }) port = self.args['port'] p = yield AppProperty.find( where=['application_id = ? AND port = ?', app.id, port], limit=1) if p is None: abort(404, message={'error': "Application property doesn't exist."}) kwargs = {} for a, v in self.args.items(): if a not in {'name', 'type', 'port'}: continue if v is not None and v != getattr(p, a): kwargs[a] = v setattr(p, a, v) (valid, message) = yield p.valid() if not valid: abort(400, message=message) # Update the model if kwargs: p.update(**kwargs) returnValue(({}, 200)) except TimeoutError: log.error( "REST API timeout put request for application {appeui} " "property {port}", appeui=euiString(appeui), port=port)
def delete(self, host): """Method to handle gateway DELETE requests Args: host (str): Gateway host """ try: g = yield Gateway.find(where=['host = ?', host], limit=1) # Return a 404 if not found. if g is None: abort(404, message={'error': "Gateway {} doesn't exist.".format(host)}) deleted = yield g.delete() self.server.lora.deleteGateway(g) returnValue(({}, 200)) except TimeoutError: log.error("REST API timeout retrieving gateway {host}", host=host)
def get(self): """Method to get all application interfaces""" try: interfaces = interfaceManager.getAllInterfaces() if interfaces is None: returnValue({}) marshal_fields = { 'type': fields.String(attribute='__class__.__name__'), 'id': fields.Integer(attribute='appinterface.id'), 'name': fields.String } data = {} for i, interface in enumerate(interfaces): data[i] = marshal(interface, marshal_fields) returnValue(data) yield except TimeoutError: log.error("REST API timeout retrieving application interfaces")
def put(self): """Method to handle system configuration PUT requests Args: appeui (int): Application EUI """ try: config = yield Config.find(limit=1) # Return a 404 if not found. if config is None: abort(404, message={'error': "Could not get the system configuration"}) # Filter args params = {k: v for k, v in self.args.iteritems() if v is not None} # Set the new attributes for a,v in params.items(): setattr(config, a, v) # Validate the configuration (valid, message) = config.check() if not valid: abort(400, message=message) # Reload the config (success, message) = self.server.reload(config) if not success: abort(500, message=message) yield config.save() returnValue(({}, 200)) except TimeoutError: log.error("REST API timeout retrieving application {appeui}", appeui=euiString(appeui)) except TimeoutError: log.error("REST API timeout retrieving application {appeui}", appeui=euiString(appeui))
def put(self, appinterface_id): """Method to handle AppInterface PUT requests Args: appinterface_id (int): Application Interface ID """ try: interface = interfaceManager.getInterface(appinterface_id) # Return a 404 if not found. if interface is None: abort(404, message={ 'error': "Application interface id {} " "doesn't exist.".format(str(appinterface_id)) }) kwargs = {} for a, v in self.args.items(): if hasattr(interface, a) and v is not None and v != getattr(interface, a): kwargs[a] = v setattr(interface, a, v) (valid, message) = yield interface.valid() if not valid: abort(400, message=message) # Update the interface via interfaceManager if kwargs: yield interfaceManager.updateInterface(interface) returnValue(({}, 200)) except TimeoutError: log.error( "REST API timeout retrieving application interface " "{id}", id=appinterface_id)
def delete(self, appeui): """Method to handle application property DELETE requests Args: appeui (int): application EUI """ try: # Return a 404 if the application is not found. app = yield Application.find(where=['appeui = ?', appeui], limit=1) if app is None: abort(404, message={ 'error': "Application {} doesn't exist.".format( euiString(appeui)) }) port = self.args['port'] # Return a 404 if the property is not found. p = yield AppProperty.find( where=['application_id = ? AND port = ?', app.id, port], limit=1) if p is None: abort(404, message={'error': "Application property doesn't exist."}) yield p.delete() returnValue(({}, 200)) except TimeoutError: log.error( "REST API timeout retrieving application {appeui} " "property {port}", appeui=euiString(appeui), port=port)
def get(self, appeui): """Method to handle application GET requests Args: appeui (int): Application EUI """ try: a = yield Application.find(where=['appeui = ?', appeui], limit=1) # Return a 404 if not found. if a is None: abort(404, message={ 'error': "Application {} doesn't exist.".format( euiString(appeui)) }) data = marshal(a, self.fields) data['properties'] = yield self.getProperties(a) returnValue(data) except TimeoutError: log.error("REST API timeout retrieving application {appeui}", appeui=euiString(appeui))
def parseConfig(self, cfile): """Parse the database configuration file Args: cfile (str): Configuration file path Returns: True on success, otherwise False """ # Check file exists if not os.path.exists(cfile): log.error("Can't find database configuration file {cfile}", cfile=cfile) return False elif not os.path.isfile(cfile): log.error("Can't read database configuration file {cfile}", cfile=cfile) return False try: self.parser.read(cfile) except ConfigParser.ParsingError: log.error("Error parsing configuration file {cfile}", cfile=cfile) return False # Get sections sections = self.parser.sections() # Database section if 'database' not in sections: log.error( "Couldn't find the [database] section in the configuration file" ) return False options = [ Option('host', 'str', default=False), Option('user', 'str', default=False), Option('password', 'str', default=False), Option('database', 'str', default=False), ] for option in options: if not self._getOption('database', option, self): return False return True
def inboundAppMessage(self, devaddr, appdata, acknowledge=False): """Sends inbound data from the application interface to the device Args: devaddr (int): 32 bit device address (DevAddr) appdata (str): packed application data acknowledge (bool): Acknowledged message """ log.info("Inbound message to devaddr {devaddr}", devaddr=devaddrString(devaddr)) # Retrieve the active device device = yield self._getActiveDevice(devaddr) if device is None: log.error("Cannot send to unregistered device address {devaddr}", devaddr=devaddrString(devaddr)) returnValue(None) # Check the device is enabled if not device.enabled: log.error( "Inbound application message for disabled device " "{deveui}", deveui=euiString(device.deveui)) returnValue(None) # Get the associated application app = yield Application.find(where=['appeui = ?', device.appeui], limit=1) if app is None: log.error( "Inbound application message for {deveui} - " "AppEUI {appeui} does not match any configured applications.", deveui=euiString(device.deveui), appeui=device.appeui) returnValue(None) # Find the gateway gateway = self.lora.gateway(device.gw_addr) if gateway is None: log.error( "Could not find gateway for inbound message to " "{devaddr}.", devaddr=devaddrString(device.devaddr)) returnValue(None) # Increment fcntdown fcntdown = device.fcntdown + 1 # Piggyback any queued MAC messages in fopts fopts = '' device.rx = self.band.rxparams((device.tx_chan, device.tx_datr), join=False) if self.config.macqueueing: # Get all of this device's queued commands: this returns a list of tuples (index, command) commands = [(i, c[2]) for i, c in enumerate(self.commands) if device.deveui == c[1]] for (index, command) in commands: # Check if we can accommodate the command. If so, encode and remove from the queue if self.band.checkAppPayloadLen(device.rx[1]['datr'], len(fopts) + len(appdata)): fopts += command.encode() del self.commands[index] else: break # Create the downlink message, encrypt with AppSKey and encode response = MACDataDownlinkMessage(device.devaddr, device.nwkskey, device.fcntdown, self.config.adrenable, fopts, int(app.fport), appdata, acknowledge=acknowledge) response.encrypt(device.appskey) data = response.encode() # Create Txpk objects txpk = self._txpkResponse(device, data, gateway, itmst=int(device.tmst), immediate=False) request = GatewayMessage(version=2, gatewayEUI=gateway.eui, remote=(gateway.host, gateway.port)) # Save the frame count down device.update(fcntdown=fcntdown) # Send RX1 window message self.lora.sendPullResponse(request, txpk[1]) # If Class A, send the RX2 window message self.lora.sendPullResponse(request, txpk[2])
def processPushDataMessage(self, request, gateway): """Process a PUSH_DATA message from a LoraWAN gateway Args: request (GatewayMessage): the received gateway message object gateway (Gateway): the gateway that sent the message Returns: True on success, otherwise False """ if not request.rxpk: request.rxpk = [] for rxpk in request.rxpk: # Decode the MAC message message = MACMessage.decode(rxpk.data) if message is None: log.info( "MAC message decode error for gateway {gateway}: message " "timestamp {timestamp}", gateway=gateway.host, timestamp=str(rxpk.time)) returnValue(False) # Check if thisis a duplicate message if self._checkDuplicateMessage(message): returnValue(False) # Join Request if message.isJoinRequest(): # Get the application using appeui app = yield Application.find( where=['appeui = ?', message.appeui], limit=1) #app = next((a for a in self.applications if # a.appeui == message.appeui), None) if app is None: log.info( "Message from {deveui} - AppEUI {appeui} " "does not match any configured applications.", deveui=euiString(message.deveui), appeui=message.appeui) returnValue(False) # Find the Device device = yield Device.find( where=['deveui = ?', message.deveui], limit=1) if device is None: #log.info("Message from unregistered device {deveui}", # deveui=euiString(message.deveui)) #returnValue(False) # TODO save device to database (cheng) device = Device(deveui=message.deveui, name='smk_node', devclass='A', enabled=True, otaa=True, devaddr=None, devnonce=[], appeui=message.appeui, nwkskey='', appskey='', fcntup=0, fcntdown=0, fcnterror=False, snr=[], snr_average=0) #device.save() else: # Check the device is enabled if not device.enabled: log.info("Join request for disabled device {deveui}.", deveui=euiString(device.deveui)) returnValue(False) # Process join request joined = yield self._processJoinRequest(message, app, device) if joined: # Update the ADR measures if self.config.adrenable: device.updateSNR(rxpk.lsnr) yield device.update(tx_chan=rxpk.chan, tx_datr=rxpk.datr, devaddr=device.devaddr, nwkskey=device.nwkskey, appskey=device.appskey, time=rxpk.time, tmst=rxpk.tmst, gw_addr=gateway.host, fcntup=0, fcntdown=0, fcnterror=False, devnonce=device.devnonce, snr=device.snr, snr_average=device.snr_average) log.info( "Successful Join request from DevEUI {deveui} " "for AppEUI {appeui} | Assigned address {devaddr}", deveui=euiString(device.deveui), appeui=euiString(app.appeui), devaddr=devaddrString(device.devaddr)) # Send the join response device.save() self._sendJoinResponse(request, rxpk, gateway, app, device) returnValue(True) else: log.info( "Could not process join request from device " "{deveui}.", deveui=euiString(device.deveui)) returnValue(False) # LoRa message. Check this is a registered device device = yield self._getActiveDevice(message.payload.fhdr.devaddr) if device is None: log.info( "Message from device using unregistered address " "{devaddr}", devaddr=devaddrString(message.payload.fhdr.devaddr)) returnValue(False) # Check the device is enabled if not device.enabled: log.info("Message from disabled device {devaddr}", devaddr=devaddrString(message.payload.fhdr.devaddr)) returnValue(False) # Check frame counter if not device.checkFrameCount(message.payload.fhdr.fcnt, self.band.max_fcnt_gap, self.config.fcrelaxed): log.info("Message from {devaddr} failed frame count check.", devaddr=devaddrString(message.payload.fhdr.devaddr)) log.debug( "Received frame count {fcnt}, device frame count {dfcnt}", fcnt=message.payload.fhdr.fcnt, dfcnt=device.fcntup) yield device.update(fcntup=device.fcntup, fcntdown=device.fcntdown, fcnterror=device.fcnterror) returnValue(False) # Perform message integrity check. if not message.checkMIC(device.nwkskey): log.info( "Message from {devaddr} failed message " "integrity check.", devaddr=devaddrString(message.payload.fhdr.devaddr)) returnValue(False) # Update SNR reading and device device.updateSNR(rxpk.lsnr) yield device.update(tx_chan=rxpk.chan, tx_datr=rxpk.datr, fcntup=device.fcntup, fcntdown=device.fcntdown, fcnterror=device.fcnterror, time=rxpk.time, tmst=rxpk.tmst, adr=bool(message.payload.fhdr.adr), snr=device.snr, snr_average=device.snr_average, gw_addr=gateway.host) # Set the device rx window parameters device.rx = self.band.rxparams((device.tx_chan, device.tx_datr), join=False) # Process MAC Commands commands = [] # Standalone MAC command if message.isMACCommand(): message.decrypt(device.nwkskey) commands = [MACCommand.decode(message.payload.frmpayload)] # Contains piggybacked MAC command(s) elif message.hasMACCommands(): commands = message.commands for command in commands: if command.isLinkCheckReq(): self._processLinkCheckReq(device, command, request, rxpk.lsnr) elif command.isLinkADRAns(): self._processLinkADRAns(device, command) # TODO: add other MAC commands # Process application data message if message.isUnconfirmedDataUp() or message.isConfirmedDataUp(): # Find the app app = yield Application.find( where=['appeui = ?', device.appeui], limit=1) if app is None: log.info( "Message from {devaddr} - AppEUI {appeui} " "does not match any configured applications.", devaddr=euiString(device.devaddr), appeui=device.appeui) returnValue(False) # Decrypt frmpayload message.decrypt(device.appskey) appdata = str(message.payload.frmpayload) port = message.payload.fport # Route the data to an application server via the configured interface log.info("Outbound message from devaddr {devaddr}", devaddr=devaddrString(device.devaddr)) interface = interfaceManager.getInterface(app.appinterface_id) if interface is None: log.error( "No outbound interface found for application " "{app}", app=app.name) elif not interface.started: log.error( "Outbound interface for application " "{app} is not started", app=app.name) else: self._outboundAppMessage(interface, device, app, port, appdata) # Send an ACK if required if message.isConfirmedDataUp(): yield self.inboundAppMessage(device.devaddr, '', acknowledge=True)