Exemplo n.º 1
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))
Exemplo n.º 2
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)
Exemplo n.º 3
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))
Exemplo 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))
Exemplo n.º 5
0
def show(ctx, appeui):
    """show an application, or all applications.
    
    Args:
        ctx (Context): Click context
        appeui (str): Application EUI
    """
    if '.' in appeui:
        appeui = str(hexStringInt(str(appeui)))
    
    # Form the url and payload
    server = ctx.obj['server']
    payload = {'token': ctx.obj['token']}
    url = 'http://{}/api/v{}'.format(server, str(version))
    url += '/apps' if appeui == 'all' else '/app/{}'.format(appeui)
    
    # Make the request
    data = restRequest(server, url, 'get', payload, 200)
    if data is None:
        return
    
    # Single application
    if appeui != 'all':
        a = data
        indent = ' ' * 10
        if a['appinterface_id'] == 0:
            a['appinterface_id'] = '-'
        if a['domain'] is None:
            a['domain'] = '-'
        click.echo('Application EUI: ' + euiString(a['appeui']))
        click.echo('{}name: {}'.format(indent, a['name']))
        click.echo('{}domain: {}'.format(indent, a['domain']))
        click.echo('{}fport: {}'.format(indent, a['fport']))
        click.echo('{}interface: {}'.format(indent, a['appinterface_id']))
        if a['appinterface_id'] != '-':
            click.echo('{}Properties:'.format(indent))
            properties = sorted(a['properties'].values(), key=lambda k: k['port'])
            for p in properties:
                click.echo('{}  {}  {}:{}'.format(indent, p['port'], p['name'], p['type']))
        return
        
    # All applications
    click.echo('{:14}'.format('Application') + \
               '{:24}'.format('AppEUI') + \
               '{:15}'.format('Domain') + \
               '{:6}'.format('Fport') + \
               '{:10}'.format('Interface'))
    for i,a in data.iteritems():
        if a['appinterface_id'] == 0:
            a['appinterface_id'] = '-'
        if a['domain'] is None:
            a['domain'] = '-'
        click.echo('{:13.13}'.format(a['name']) + ' ' + \
                   '{:23}'.format(euiString(a['appeui'])) +  ' ' + \
                   '{:14.14}'.format(a['domain']) + ' ' + \
                   '{:5.5}'.format(str(a['fport'])) + ' ' + \
                   '{:10}'.format(str(a['appinterface_id'])))
Exemplo n.º 6
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)
Exemplo n.º 7
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")
Exemplo n.º 8
0
 def test_euiString(self):
     eui = int('0x0f0e0e0d00010203', 16)
     
     expected = "0f:0e:0e:0d:00:01:02:03"
     result = util.euiString(eui)
     
     self.assertEqual(expected, result)
Exemplo n.º 9
0
    def put(self, deveui):
        """Method to handle device PUT requests
        
        Args:
            deveui (int): Device deveui
        """
        try:
            device = yield Device.find(where=['deveui = ?', deveui], limit=1)
            # Return a 404 if not found.
            if device is None:
                abort(404, message={'error': "Device {} doesn't exist".
                                    format(euiString(deveui))})
            
            kwargs = {}
            for a,v in self.args.items():
                if v is not None and v != getattr(device, a):
                    kwargs [a] = v
                    setattr(device, a, v)
            (valid, message) = yield device.valid(self.server)
            if not valid:
                abort(400, message=message)
            
            # Update the device with the new attributes
            if kwargs:
                device.update(**kwargs)
            returnValue(({}, 200))

        except TimeoutError:
            log.error("REST API timeout for device PUT request")
Exemplo n.º 10
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)
Exemplo n.º 11
0
    def test_euiString(self):
        """Test euiString"""
        expected = "0f0e.0e0d.0001.0203"

        eui = int('0x0f0e0e0d00010203', 16)        
        result = util.euiString(eui)
        
        self.assertEqual(expected, result)
Exemplo n.º 12
0
def show(ctx, host):
    """show a gateway, or all gateways
    
    Args:
        ctx (Context): Click context
        host (str): Gateway IP address
    """
    # Form the url and payload
    server = ctx.obj['server']
    payload = {'token': ctx.obj['token']}
    if host == 'all':
        url = 'http://' + server + '/api/v' + str(version) + \
              '/gateways'
    else:
        url = 'http://' + server + '/api/v' + str(version) + \
              '/gateway/' + host

    # Make the request
    data = restRequest(server, url, 'get', payload, 200)
    if data is None:
        return

    # Print a gateway
    if host != 'all':
        g = data
        status = 'enabled' if g['enabled'] else 'disabled'
        indent = ' ' * 10
        click.echo(g['host'] + ': ' + g['name'])
        click.echo(indent + 'eui ' + euiString(g['eui']))
        click.echo(indent + 'power: ' + str(g['power']) + ' dBm')
        click.echo(indent + 'status: ' + status)
        return

    # Print all gateways
    click.echo('{:15}'.format('Gateway') + '{:17}'.format('IP-Address') + \
               '{:24}'.format('EUI') + \
               '{:9}'.format('Enabled') + '{:12}'.format('Power-dBm'))
    for i, g in data.iteritems():
        enabled = 'Yes' if g['enabled'] else 'No'
        click.echo('{:14.14}'.format(g['name']) + ' ' + \
                   '{:17}'.format(g['host']) + \
                   '{:24}'.format(euiString(g['eui'])) + \
                   '{:9}'.format(enabled) + \
                   '{:2}'.format(g['power']))
Exemplo n.º 13
0
    def _processJoinRequest(self, message, app, device):
        """Process an OTA Join Request message from a LoraWAN device
        
        This method checks the message devnonce and integrity code (MIC).
        If the devnonce has not been seen before, and the MIC is valid,
        we have a valid join request, and we can create the session
        keys and assign an OTA device address.
        
        Args:
            message (JoinRequestMessage): The join request message object
            app (Application): The requested application object
            device (Device): The requesting device object
            
        Returns:
            True on success, False otherwise.
        """
        # Perform devnonce check
        if not device.checkDevNonce(message):
            log.info(
                "Join request message from {deveui} failed message "
                "devnonce check.",
                deveui=euiString(message.deveui))
            returnValue(False)

        # Perform message integrity check.
        if not message.checkMIC(app.appkey):
            log.info(
                "Message from {deveui} failed message "
                "integrity check.",
                deveui=euiString(message.deveui))
            returnValue(False)

        # Assign DevEUI, NwkSkey and AppSKey.
        device.appeui = app.appeui
        device.nwkskey = self._createSessionKey(1, app, message)
        device.appskey = self._createSessionKey(2, app, message)

        # If required, obtain a OTA devaddr for the device
        if device.devaddr is None:
            device.devaddr = yield self._getFreeOTAAddress()

        returnValue(device.devaddr is not None)
Exemplo n.º 14
0
    def post(self):
        """Method to create a device"""
        deveui = self.args['deveui']
        name = self.args['name']
        devclass = self.args['devclass']
        enabled = self.args['enabled']
        otaa = self.args['otaa']
        devaddr = self.args['devaddr']
        appeui = self.args['appeui']
        nwkskey = self.args['nwkskey']
        appskey = self.args['appskey']
        
        message = {}

        # Check for required args
        required = {'deveui', 'name', 'devclass', 'enabled',
                    'otaa', 'appeui', 'devaddr', 'nwkskey', 'appskey'}
        for r in required:
            if otaa is True:
                if r in {'devaddr', 'nwkskey', 'appskey'}:
                    continue
            if self.args[r] is None:
                message[r] = "Missing the {} parameter.".format(r)
        if message:
            abort(400, message=message)

        # Check this device does not currently exist
        exists = yield Device.exists(where=['deveui = ?', deveui])
        if exists:
            message[r] = "Device {} currently exists.".format(euiString(deveui))
            abort(400, message=message)

        # Set devaddr to None if this is an otaa device
        if otaa:
            devaddr = None
        
        # Create and validate
        device = Device(deveui=deveui, name=name, devclass=devclass,
                        enabled=enabled, otaa=otaa, devaddr=devaddr,
                        appeui=appeui, nwkskey=nwkskey, appskey=appskey,
                        fcntup=0, fcntdown=0, fcnterror=False)
        (valid, message) = yield device.valid(self.server)
        if not valid:
            abort(400, message=message)

        try:
            d = yield device.save()
            if d is None:
                abort(500, message={'error': "Error saving device"})
            location = self.restapi.api.prefix + '/device/' + str(device.deveui)
            returnValue(({}, 201, {'Location': location}))
        except TimeoutError:
            # Exception returns 500 to client
            log.error("REST API timeout for device POST request")
Exemplo n.º 15
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)
Exemplo n.º 16
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))
Exemplo n.º 17
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))
Exemplo n.º 18
0
    def get(self, deveui):
        """Method to handle device GET request
        
        Args:
            deveui (int): Device deveui
        """
        try:
            d = yield Device.find(where=['deveui = ?', deveui], limit=1)
            # Return a 404 if not found.
            if d is None:
               abort(404, message={'error': "Device {} doesn't exist".
                                   format(euiString(deveui))})
            returnValue(marshal(d, self.fields))

        except TimeoutError:
            log.error("REST API timeout for device GET request")
Exemplo n.º 19
0
    def delete(self, deveui):
        """Method to handle device DELETE requests
        
        Args:
            deveui (int): Device deveui
        """
        try:
            d = yield Device.find(where=['deveui = ?', deveui], limit=1)
            # Return a 404 if not found.
            if d is None:
                abort(404, message={'error': "Device {} doesn't exist".
                                    format(euiString(deveui))})
            deleted = yield d.delete()
            returnValue(({}, 200))

        except TimeoutError:
            log.error("REST API timeout for device DELETE request")
Exemplo n.º 20
0
def show(ctx, deveui):
    """show a device, or all devices.
    
    Args:
        ctx (Context): Click context
        deveui (str): Device EUI string
    """
    if '.' in deveui:
        deveui = str(hexStringInt(str(deveui)))
    
    # Form the url and payload
    server = ctx.obj['server']
    payload = {'token': ctx.obj['token']}
    url = 'http://{}/api/v{}'.format(server, version)
    url += '/devices' if deveui == 'all' else '/device/{}'.format(deveui)
    
    # Make the request
    data = restRequest(server, url, 'get', payload, 200)
    if data is None:
        return
    
    # Single device
    if deveui != 'all':
        d = data
        indent = ' ' * 10
        enable = 'enabled' if d['enabled'] else 'disabled'
        drate = d['tx_datr'] if d['tx_datr'] else 'N/A'
        nwkid = hex(d['devaddr'] >> 25)
        snrav = '{0:.2f} dBm'.format(d['snr_average']) if d['snr_average'] else 'N/A'
        appname = d['appname'] if d['appname'] else 'N/A'
        lat = '{0:.4f}'.format(d['latitude']) if d['latitude'] else 'N/A'
        lon = '{0:.4f}'.format(d['longitude']) if d['longitude'] else 'N/A'
        activ = 'Over the air (OTAA)' if d['otaa'] else 'Personalization (ABP)'
        click.echo('Device EUI: ' + euiString(d['deveui']))
        click.echo(indent + 'device address ' + devaddrString(d['devaddr']) + \
                   ' nwkID ' + nwkid + ' ' + enable)
        click.echo(indent + 'name: ' + d['name'])
        click.echo(indent + 'class: ' + d['devclass'].upper())
        click.echo(indent + 'application EUI: ' + euiString(d['appeui']))
        click.echo(indent + 'activation: ' + activ)
        click.echo(indent + 'appname: ' + appname)
        click.echo(indent + 'latitude: ' + lat)
        click.echo(indent + 'longitude: ' + lon)
        if not d['otaa']:
            click.echo(indent + 'appskey: ' + intHexString(d['appskey'], 16))
            click.echo(indent + 'nwkskey: ' + intHexString(d['nwkskey'], 16))
        click.echo(indent + 'data rate: ' + drate)
        click.echo(indent + 'average SNR: ' + snrav)
        return
        
    # All devices
    click.echo('{:15}'.format('Device') + \
               '{:24}'.format('DeviceEUI') + \
               '{:12}'.format('DevAddr') + \
               '{:9}'.format('Enabled') + \
               '{:5}'.format('Act') + \
               '{:12}'.format('Average-SNR'))
    for i,d in data.iteritems():
        enable = 'Yes' if d['enabled'] else 'No'
        active = 'OTA' if d['otaa'] else 'ABP'
        snravg = '{0:.2f} dBm'.format(d['snr_average']) if d['snr_average'] else 'N/A'
        click.echo('{:14.14}'.format(d['name']) + ' ' + \
                   '{:23}'.format(euiString(d['deveui'])) +  ' ' + \
                   '{:12}'.format(devaddrString(d['devaddr'])) + \
                   '{:9}'.format(enable) + \
                   '{:5}'.format(active) + \
                   '{:12}'.format(snravg))
Exemplo n.º 21
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])
Exemplo n.º 22
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)