Ejemplo n.º 1
0
    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")
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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))
Ejemplo n.º 5
0
    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")
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
    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))
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
    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)
Ejemplo n.º 11
0
    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
Ejemplo n.º 12
0
    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))
Ejemplo n.º 13
0
    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)
Ejemplo n.º 14
0
    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))
Ejemplo n.º 15
0
    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)
Ejemplo n.º 16
0
 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)
Ejemplo n.º 17
0
 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))
Ejemplo n.º 18
0
 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")
Ejemplo n.º 19
0
    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)
Ejemplo n.º 20
0
    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))
Ejemplo n.º 21
0
    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)
Ejemplo n.º 22
0
    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)
Ejemplo n.º 23
0
    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")
Ejemplo n.º 24
0
    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))
Ejemplo n.º 25
0
    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)
Ejemplo n.º 26
0
    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)
Ejemplo n.º 27
0
    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))
Ejemplo n.º 28
0
    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
Ejemplo n.º 29
0
    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])
Ejemplo n.º 30
0
    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)