예제 #1
0
// NewRedisHandler creates a new Redis-backed Handler
func NewRedisHandler(client *redis.Client, ttnBrokerID string) Handler {
	return &handler{
		devices:      device.NewRedisDeviceStore(client, "handler"),
		applications: application.NewRedisApplicationStore(client, "handler"),
		ttnBrokerID:  ttnBrokerID,
	}
}
예제 #2
0
func TestEnqueueDownlink(t *testing.T) {
	a := New(t)
	appID := "app1"
	devID := "dev1"
	h := &handler{
		Component: &component.Component{Ctx: GetLogger(t, "TestEnqueueDownlink")},
		devices:   device.NewRedisDeviceStore(GetRedisClient(), "handler-test-enqueue-downlink"),
		mqttEvent: make(chan *types.DeviceEvent, 10),
	}
	err := h.EnqueueDownlink(&types.DownlinkMessage{
		AppID: appID,
		DevID: devID,
	})
	a.So(err, ShouldNotBeNil)
	h.devices.Set(&device.Device{
		AppID: appID,
		DevID: devID,
	})
	defer func() {
		h.devices.Delete(appID, devID)
	}()
	err = h.EnqueueDownlink(&types.DownlinkMessage{
		AppID: appID,
		DevID: devID,
		PayloadFields: map[string]interface{}{
			"string": "hello!",
			"int":    42,
			"bool":   true,
		},
	})
	a.So(err, ShouldBeNil)
	dev, _ := h.devices.Get(appID, devID)
	a.So(dev.NextDownlink, ShouldNotBeEmpty)
	a.So(dev.NextDownlink.PayloadFields, ShouldHaveLength, 3)
}
예제 #3
0
func TestConvertFromLoRaWAN(t *testing.T) {
	a := New(t)
	h := &handler{
		devices:   device.NewRedisDeviceStore(GetRedisClient(), "handler-test-convert-from-lorawan"),
		Component: &component.Component{Ctx: GetLogger(t, "TestConvertFromLoRaWAN")},
		mqttEvent: make(chan *types.DeviceEvent, 10),
	}
	h.devices.Set(&device.Device{
		DevID: "devid",
		AppID: "appid",
	})
	defer func() {
		h.devices.Delete("appid", "devid")
	}()
	ttnUp, appUp := buildLorawanUplink([]byte{0x40, 0x04, 0x03, 0x02, 0x01, 0x20, 0x01, 0x00, 0x0A, 0x46, 0x55, 0x96, 0x42, 0x92, 0xF2})
	err := h.ConvertFromLoRaWAN(h.Ctx, ttnUp, appUp)
	a.So(err, ShouldBeNil)
	a.So(appUp.PayloadRaw, ShouldResemble, []byte{0xaa, 0xbc})
	a.So(appUp.FCnt, ShouldEqual, 1)
}
예제 #4
0
func TestConvertToLoRaWAN(t *testing.T) {
	a := New(t)
	h := &handler{
		devices:   device.NewRedisDeviceStore(GetRedisClient(), "handler-test-convert-to-lorawan"),
		Component: &component.Component{Ctx: GetLogger(t, "TestConvertToLoRaWAN")},
	}
	h.devices.Set(&device.Device{
		DevID: "devid",
		AppID: "appid",
	})
	defer func() {
		h.devices.Delete("appid", "devid")
	}()
	appDown, ttnDown := buildLorawanDownlink([]byte{0xaa, 0xbc})
	err := h.ConvertToLoRaWAN(h.Ctx, appDown, ttnDown)
	a.So(err, ShouldBeNil)
	a.So(ttnDown.Payload, ShouldResemble, []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0xa1, 0x33, 0x68, 0x0A, 0x08, 0xBD})

	appDown, ttnDown = buildLorawanDownlink([]byte{0xaa, 0xbc})
	appDown.FPort = 8
	err = h.ConvertToLoRaWAN(h.Ctx, appDown, ttnDown)
	a.So(err, ShouldBeNil)
	a.So(ttnDown.Payload, ShouldResemble, []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x01, 0x00, 0x08, 0xa1, 0x33, 0x41, 0xA9, 0xFA, 0x03})
}
예제 #5
0
func TestHandleUplink(t *testing.T) {
	a := New(t)
	var err error
	var wg WaitGroup
	appEUI := types.AppEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8})
	appID := "appid"
	devEUI := types.DevEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8})
	devID := "devid"
	h := &handler{
		Component:    &component.Component{Ctx: GetLogger(t, "TestHandleUplink")},
		devices:      device.NewRedisDeviceStore(GetRedisClient(), "handler-test-handle-uplink"),
		applications: application.NewRedisApplicationStore(GetRedisClient(), "handler-test-handle-uplink"),
	}
	h.InitStatus()
	dev := &device.Device{
		AppID:  appID,
		DevID:  devID,
		AppEUI: appEUI,
		DevEUI: devEUI,
	}
	h.devices.Set(dev)
	defer func() {
		h.devices.Delete(appID, devID)
	}()
	h.applications.Set(&application.Application{
		AppID: appID,
	})
	defer func() {
		h.applications.Delete(appID)
	}()
	h.mqttUp = make(chan *types.UplinkMessage)
	h.mqttEvent = make(chan *types.DeviceEvent, 10)
	h.downlink = make(chan *pb_broker.DownlinkMessage)

	uplink, _ := buildLorawanUplink([]byte{0x40, 0x04, 0x03, 0x02, 0x01, 0x00, 0x01, 0x00, 0x0A, 0x4D, 0xDA, 0x23, 0x99, 0x61, 0xD4})

	downlinkEmpty := []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x21, 0xEA, 0x8B, 0x0E}
	downlinkACK := []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x20, 0x00, 0x00, 0x0A, 0x3B, 0x3F, 0x77, 0x0B}
	downlinkMAC := []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x05, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, 0x0A, 0x4D, 0x11, 0x55, 0x01}
	expected := []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x66, 0xE6, 0x1D, 0x49, 0x82, 0x84}

	downlink := &pb_broker.DownlinkMessage{
		DownlinkOption: &pb_broker.DownlinkOption{
			ProtocolConfig: &pb_protocol.TxConfiguration{Protocol: &pb_protocol.TxConfiguration_Lorawan{Lorawan: &pb_lorawan.TxConfiguration{
				FCnt: 0,
			}}},
		},
	}

	// Test Uplink, no downlink option available
	wg.Add(1)
	go func() {
		<-h.mqttUp
		wg.Done()
	}()
	err = h.HandleUplink(uplink)
	a.So(err, ShouldBeNil)
	wg.WaitFor(50 * time.Millisecond)

	uplink.ResponseTemplate = downlink

	// Test Uplink, no downlink needed
	wg.Add(1)
	go func() {
		<-h.mqttUp
		wg.Done()
	}()
	downlink.Payload = downlinkEmpty
	err = h.HandleUplink(uplink)
	a.So(err, ShouldBeNil)
	wg.WaitFor(50 * time.Millisecond)

	// Test Uplink, ACK downlink needed
	wg.Add(2)
	go func() {
		<-h.mqttUp
		wg.Done()
	}()
	go func() {
		<-h.downlink
		wg.Done()
	}()
	downlink.Payload = downlinkACK
	err = h.HandleUplink(uplink)
	a.So(err, ShouldBeNil)
	wg.WaitFor(50 * time.Millisecond)

	// Test Uplink, MAC downlink needed
	wg.Add(2)
	go func() {
		<-h.mqttUp
		wg.Done()
	}()
	go func() {
		<-h.downlink
		wg.Done()
	}()
	downlink.Payload = downlinkMAC
	err = h.HandleUplink(uplink)
	a.So(err, ShouldBeNil)
	wg.WaitFor(50 * time.Millisecond)

	dev.StartUpdate()
	dev.NextDownlink = &types.DownlinkMessage{
		PayloadRaw: []byte{0xaa, 0xbc},
	}

	// Test Uplink, Data downlink needed
	h.devices.Set(dev)
	wg.Add(2)
	go func() {
		<-h.mqttUp
		wg.Done()
	}()
	go func() {
		dl := <-h.downlink
		a.So(dl.Payload, ShouldResemble, expected)
		wg.Done()
	}()
	downlink.Payload = downlinkEmpty
	err = h.HandleUplink(uplink)
	a.So(err, ShouldBeNil)
	wg.WaitFor(50 * time.Millisecond)

	dev, _ = h.devices.Get(appID, devID)
	a.So(dev.NextDownlink, ShouldBeNil)
}
예제 #6
0
func TestHandleMQTT(t *testing.T) {
	host := os.Getenv("MQTT_ADDRESS")
	if host == "" {
		host = "localhost:1883"
	}

	a := New(t)
	var wg WaitGroup
	c := mqtt.NewClient(apex.Wrap(GetLogger(t, "TestHandleMQTT")), "test", "", "", fmt.Sprintf("tcp://%s", host))
	err := c.Connect()
	a.So(err, ShouldBeNil)
	appID := "handler-mqtt-app1"
	devID := "handler-mqtt-dev1"
	h := &handler{
		Component: &component.Component{Ctx: GetLogger(t, "TestHandleMQTT")},
		devices:   device.NewRedisDeviceStore(GetRedisClient(), "handler-test-handle-mqtt"),
	}
	h.devices.Set(&device.Device{
		AppID: appID,
		DevID: devID,
	})
	defer func() {
		h.devices.Delete(appID, devID)
	}()
	err = h.HandleMQTT("", "", fmt.Sprintf("tcp://%s", host))
	a.So(err, ShouldBeNil)

	c.PublishDownlink(types.DownlinkMessage{
		AppID:      appID,
		DevID:      devID,
		PayloadRaw: []byte{0xAA, 0xBC},
	}).Wait()
	<-time.After(50 * time.Millisecond)
	dev, _ := h.devices.Get(appID, devID)
	a.So(dev.NextDownlink, ShouldNotBeNil)

	wg.Add(1)
	c.SubscribeDeviceUplink(appID, devID, func(client mqtt.Client, r_appID string, r_devID string, req types.UplinkMessage) {
		a.So(r_appID, ShouldEqual, appID)
		a.So(r_devID, ShouldEqual, devID)
		a.So(req.PayloadRaw, ShouldResemble, []byte{0xAA, 0xBC})
		wg.Done()
	}).Wait()

	h.mqttUp <- &types.UplinkMessage{
		DevID:      devID,
		AppID:      appID,
		PayloadRaw: []byte{0xAA, 0xBC},
		PayloadFields: map[string]interface{}{
			"field": "value",
		},
	}

	wg.Add(1)
	c.SubscribeDeviceActivations(appID, devID, func(client mqtt.Client, r_appID string, r_devID string, req types.Activation) {
		a.So(r_appID, ShouldEqual, appID)
		a.So(r_devID, ShouldEqual, devID)
		wg.Done()
	}).Wait()

	h.mqttEvent <- &types.DeviceEvent{
		DevID: devID,
		AppID: appID,
		Event: types.ActivationEvent,
	}

	a.So(wg.WaitFor(200*time.Millisecond), ShouldBeNil)
}
예제 #7
0
func TestHandleActivation(t *testing.T) {
	a := New(t)

	h := &handler{
		Component:    &component.Component{Ctx: GetLogger(t, "TestHandleActivation")},
		applications: application.NewRedisApplicationStore(GetRedisClient(), "handler-test-activation"),
		devices:      device.NewRedisDeviceStore(GetRedisClient(), "handler-test-activation"),
	}
	h.InitStatus()
	h.mqttEvent = make(chan *types.DeviceEvent, 10)
	var wg WaitGroup

	appEUI := types.AppEUI{1, 2, 3, 4, 5, 6, 7, 8}
	appID := appEUI.String()
	devEUI := types.DevEUI{1, 2, 3, 4, 5, 6, 7, 8}
	devID := devEUI.String()
	unknownDevEUI := types.DevEUI{8, 7, 6, 5, 4, 3, 2, 1}

	appKey := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}

	h.applications.Set(&application.Application{
		AppID: appID,
	})
	defer func() {
		h.applications.Delete(appID)
	}()

	h.devices.Set(&device.Device{
		AppID:  appID,
		DevID:  devID,
		AppEUI: appEUI,
		DevEUI: devEUI,
		AppKey: appKey,
	})
	defer func() {
		h.devices.Delete(appID, devID)
	}()

	// Unknown
	res, err := doTestHandleActivation(h,
		appEUI,
		unknownDevEUI,
		[2]byte{1, 2},
		appKey,
	)
	a.So(err, ShouldNotBeNil)
	a.So(res, ShouldBeNil)

	// Wrong AppKey
	res, err = doTestHandleActivation(h,
		appEUI,
		devEUI,
		[2]byte{1, 2},
		[16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	)
	a.So(err, ShouldNotBeNil)
	a.So(res, ShouldBeNil)

	wg.Add(1)
	go func() {
		<-h.mqttEvent
		wg.Done()
	}()

	// Known
	res, err = doTestHandleActivation(h,
		appEUI,
		devEUI,
		[2]byte{1, 2},
		appKey,
	)
	a.So(err, ShouldBeNil)
	a.So(res, ShouldNotBeNil)

	wg.WaitFor(50 * time.Millisecond)

	// Same DevNonce used twice
	res, err = doTestHandleActivation(h,
		appEUI,
		devEUI,
		[2]byte{1, 2},
		appKey,
	)
	a.So(err, ShouldNotBeNil)
	a.So(res, ShouldBeNil)

	wg.Add(1)
	go func() {
		<-h.mqttEvent
		wg.Done()
	}()

	// Other DevNonce
	res, err = doTestHandleActivation(h,
		appEUI,
		devEUI,
		[2]byte{2, 1},
		appKey,
	)
	a.So(err, ShouldBeNil)
	a.So(res, ShouldNotBeNil)

	wg.WaitFor(50 * time.Millisecond)

	// TODO: Validate response

	// TODO: Check DB

}
예제 #8
0
func TestHandleDownlink(t *testing.T) {
	a := New(t)
	var err error
	var wg WaitGroup
	appID := "app2"
	devID := "dev2"
	appEUI := types.AppEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8})
	devEUI := types.DevEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8})
	h := &handler{
		Component:    &component.Component{Ctx: GetLogger(t, "TestHandleDownlink")},
		devices:      device.NewRedisDeviceStore(GetRedisClient(), "handler-test-handle-downlink"),
		applications: application.NewRedisApplicationStore(GetRedisClient(), "handler-test-enqueue-downlink"),
		downlink:     make(chan *pb_broker.DownlinkMessage),
		mqttEvent:    make(chan *types.DeviceEvent, 10),
	}
	h.InitStatus()
	// Neither payload nor Fields provided : ERROR
	err = h.HandleDownlink(&types.DownlinkMessage{
		AppID: appID,
		DevID: devID,
	}, &pb_broker.DownlinkMessage{
		AppEui: &appEUI,
		DevEui: &devEUI,
	})
	a.So(err, ShouldNotBeNil)

	h.devices.Set(&device.Device{
		AppID: appID,
		DevID: devID,
	})
	defer func() {
		h.devices.Delete(appID, devID)
	}()
	err = h.HandleDownlink(&types.DownlinkMessage{
		AppID: appID,
		DevID: devID,
	}, &pb_broker.DownlinkMessage{
		AppEui:  &appEUI,
		DevEui:  &devEUI,
		Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0},
	})
	a.So(err, ShouldBeNil)

	// Payload provided
	wg.Add(1)
	go func() {
		dl := <-h.downlink
		a.So(dl.Payload, ShouldNotBeEmpty)
		wg.Done()
	}()
	err = h.HandleDownlink(&types.DownlinkMessage{
		AppID:      appID,
		DevID:      devID,
		PayloadRaw: []byte{0xAA, 0xBC},
	}, &pb_broker.DownlinkMessage{
		AppEui:         &appEUI,
		DevEui:         &devEUI,
		Payload:        []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0},
		DownlinkOption: &pb_broker.DownlinkOption{},
	})
	a.So(err, ShouldBeNil)
	wg.WaitFor(100 * time.Millisecond)

	// Both Payload and Fields provided
	h.applications.Set(&application.Application{
		AppID: appID,
		Encoder: `function Encoder (payload){
	  		return [96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0]
			}`,
	})
	defer func() {
		h.applications.Delete(appID)
	}()
	jsonFields := map[string]interface{}{"temperature": 11}
	err = h.HandleDownlink(&types.DownlinkMessage{
		FPort:         1,
		AppID:         appID,
		DevID:         devID,
		PayloadFields: jsonFields,
		PayloadRaw:    []byte{0xAA, 0xBC},
	}, &pb_broker.DownlinkMessage{
		AppEui:  &appEUI,
		DevEui:  &devEUI,
		Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0},
	})
	a.So(err, ShouldNotBeNil)

	// JSON Fields provided
	wg.Add(1)
	go func() {
		dl := <-h.downlink
		a.So(dl.Payload, ShouldNotBeEmpty)
		wg.Done()
	}()
	err = h.HandleDownlink(&types.DownlinkMessage{
		FPort:         1,
		AppID:         appID,
		DevID:         devID,
		PayloadFields: jsonFields,
	}, &pb_broker.DownlinkMessage{
		AppEui:         &appEUI,
		DevEui:         &devEUI,
		Payload:        []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0},
		DownlinkOption: &pb_broker.DownlinkOption{},
	})
	a.So(err, ShouldBeNil)
	wg.WaitFor(100 * time.Millisecond)
}