From 7ee7d4df898895f628eced7b9c6a05808fbef03c Mon Sep 17 00:00:00 2001 From: Wolfgang Hottgenroth Date: Fri, 15 Dec 2023 11:44:35 +0100 Subject: [PATCH] Dragino parser --- src/udi/config-level.json | 23 +++++ .../ttn/models/draginoLdds75/draginoLdds75.go | 61 ++++++++++++++ .../models/draginoLdds75/example-ldds75.json | 83 +++++++++++++++++++ .../models/draginoLmds200/draginoLmds200.go | 59 +++++++++++++ .../draginoLmds200}/lmds200-example.json | 0 .../emuProfIILoRaCfg1/emuProfIILoRaCfg1.go | 36 ++++---- src/udi/handlers/ttn/ttn.go | 31 ++++--- 7 files changed, 263 insertions(+), 30 deletions(-) create mode 100644 src/udi/config-level.json create mode 100644 src/udi/handlers/ttn/models/draginoLdds75/draginoLdds75.go create mode 100644 src/udi/handlers/ttn/models/draginoLdds75/example-ldds75.json create mode 100644 src/udi/handlers/ttn/models/draginoLmds200/draginoLmds200.go rename src/udi/handlers/ttn/{ => models/draginoLmds200}/lmds200-example.json (100%) diff --git a/src/udi/config-level.json b/src/udi/config-level.json new file mode 100644 index 0000000..30cd0e8 --- /dev/null +++ b/src/udi/config-level.json @@ -0,0 +1,23 @@ +{ + "mqtt": { + "broker": "ssl://eu1.cloud.thethings.network:8883", + "username": "de-hottis-level-monitoring@ttn", + "password": "ENV", + "tlsEnable": "true" + }, + "topicMappings": [ + { + "topics": [ "v3/#" ], + "handler": "TTN", + "id": "TTN0", + "config": { + "databaseConnStr": "", + "attributes": { + } + } + } + ], + "archiver": { + "dir": "./tmp/udi" + } +} diff --git a/src/udi/handlers/ttn/models/draginoLdds75/draginoLdds75.go b/src/udi/handlers/ttn/models/draginoLdds75/draginoLdds75.go new file mode 100644 index 0000000..351d750 --- /dev/null +++ b/src/udi/handlers/ttn/models/draginoLdds75/draginoLdds75.go @@ -0,0 +1,61 @@ +package draginoLdds75 + +import ( + "fmt" + "strings" + "strconv" + "encoding/json" + "udi/database" +) + +/* + "decoded_payload": { + "Bat": 3.299, + "Distance": "352 mm", + "Interrupt_flag": 0, + "Sensor_flag": 1, + "TempC_DS18B20": "0.00" + }, +*/ +type message struct { + Bat float32 `json:"Bat"` + Distance string `json:"Distance"` + Interrupt_flag int `json:"Interrupt_flag"` + Sensor_flag int `json:"Sensor_flag"` + TempC_DS18B20 string `json:"TempC_DS18B20"` +} + +func Parse(fPort int, decodedPayload []byte, variables *map[string]database.VariableType) error { + if fPort != 2 { + return fmt.Errorf("Unexpected fPort %d", fPort) + } + var message message + err := json.Unmarshal(decodedPayload, &message) + if err != nil { + return fmt.Errorf("Unable to parse payload, fPort %d, error %s", fPort, err) + } + distanceParts := strings.Split(message.Distance, " ") + if len(distanceParts) != 2 && distanceParts[1] != "mm" { + return fmt.Errorf("Invalid format for distance value: %s", message.Distance) + } + distance, err2 := strconv.Atoi(distanceParts[0]) + if err2 != nil { + return fmt.Errorf("Distance value is no number: %s -> %s", message.Distance, err2) + } + (*variables)["Battery"] = database.VariableType { + Label: "Battery", + Variable: "Voltage", + Unit: "V", + Value: message.Bat, + } + (*variables)["Distance"] = database.VariableType { + Label: "Distance", + Variable: "Level", + Unit: "mm", + Value: distance, + } + return nil +} + + + diff --git a/src/udi/handlers/ttn/models/draginoLdds75/example-ldds75.json b/src/udi/handlers/ttn/models/draginoLdds75/example-ldds75.json new file mode 100644 index 0000000..1c26be0 --- /dev/null +++ b/src/udi/handlers/ttn/models/draginoLdds75/example-ldds75.json @@ -0,0 +1,83 @@ +{ + "end_device_ids": { + "device_id": "eui-a84041a14185f67f", + "application_ids": { + "application_id": "de-hottis-level-monitoring" + }, + "dev_eui": "A84041A14185F67F", + "join_eui": "A840410000000101", + "dev_addr": "260B3987" + }, + "correlation_ids": [ + "gs:uplink:01HHP8EVC1N78FGTNBTNZYGCVY" + ], + "received_at": "2023-12-15T08:11:04.142793605Z", + "uplink_message": { + "session_key_id": "AYZ/cI2YeDLCZr4urRDzCw==", + "f_port": 2, + "f_cnt": 27758, + "frm_payload": "DOMBYAAAAAE=", + "decoded_payload": { + "Bat": 3.299, + "Distance": "352 mm", + "Interrupt_flag": 0, + "Sensor_flag": 1, + "TempC_DS18B20": "0.00" + }, + "rx_metadata": [ + { + "gateway_ids": { + "gateway_id": "eui-00005813d35e3021", + "eui": "00005813D35E3021" + }, + "timestamp": 3654294763, + "rssi": -85, + "channel_rssi": -85, + "snr": 8.8, + "location": { + "latitude": 52.17065267448476, + "longitude": 7.629437184774199, + "source": "SOURCE_REGISTRY" + }, + "uplink_token": "CiIKIAoUZXVpLTAwMDA1ODEzZDM1ZTMwMjESCAAAWBPTXjAhEOu5wM4NGgwIl5TwqwYQ5Z/7vgMg+OvDp63eBA==", + "channel_index": 2, + "received_at": "2023-12-15T08:11:03.937349093Z" + } + ], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "867500000", + "timestamp": 3654294763 + }, + "received_at": "2023-12-15T08:11:03.938112475Z", + "consumed_airtime": "0.056576s", + "locations": { + "user": { + "latitude": 52.1710648323742, + "longitude": 7.62751003482794, + "altitude": 37, + "source": "SOURCE_REGISTRY" + } + }, + "version_ids": { + "brand_id": "dragino", + "model_id": "ldds75", + "hardware_version": "_unknown_hw_version_", + "firmware_version": "1.1.4", + "band_id": "EU_863_870" + }, + "network_ids": { + "net_id": "000013", + "ns_id": "EC656E0000000181", + "tenant_id": "ttn", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.network" + } + } +} diff --git a/src/udi/handlers/ttn/models/draginoLmds200/draginoLmds200.go b/src/udi/handlers/ttn/models/draginoLmds200/draginoLmds200.go new file mode 100644 index 0000000..cbcf4e7 --- /dev/null +++ b/src/udi/handlers/ttn/models/draginoLmds200/draginoLmds200.go @@ -0,0 +1,59 @@ +package draginoLmds200 + +import ( + "fmt" + "encoding/json" + "udi/database" +) + +/* + "decoded_payload": { + "Bat": 3.082, + "DALARM_count": 0, + "Distance_alarm": 0, + "Interrupt_alarm": 0, + "dis1": 105, + "dis2": 201 + }, +*/ +type message struct { + Bat float32 `json:"Bat"` + DALARM_count int `json:"DALARM_count"` + Distance_alarm int `json:"Distance_alarm"` + Interrupt_alarm int `json:"Interrupt_alarm"` + Dis1 int `json:"dis1"` + Dis2 int `json:"dis2"` +} + +func Parse(fPort int, decodedPayload []byte, variables *map[string]database.VariableType) error { + if fPort != 2 { + return fmt.Errorf("Unexpected fPort %d", fPort) + } + var message message + err := json.Unmarshal(decodedPayload, &message) + if err != nil { + return fmt.Errorf("Unable to parse payload, fPort %d, error %s", fPort, err) + } + (*variables)["Battery"] = database.VariableType { + Label: "Battery", + Variable: "Voltage", + Unit: "V", + Value: message.Bat, + } + (*variables)["Distance1"] = database.VariableType { + Label: "Distance1", + Variable: "Level", + Unit: "mm", + Value: (message.Dis1 * 10), + } + (*variables)["Distance2"] = database.VariableType { + Label: "Distance2", + Variable: "Level", + Unit: "mm", + Value: (message.Dis2 * 10), + } + return nil +} + + + diff --git a/src/udi/handlers/ttn/lmds200-example.json b/src/udi/handlers/ttn/models/draginoLmds200/lmds200-example.json similarity index 100% rename from src/udi/handlers/ttn/lmds200-example.json rename to src/udi/handlers/ttn/models/draginoLmds200/lmds200-example.json diff --git a/src/udi/handlers/ttn/models/emuProfIILoRaCfg1/emuProfIILoRaCfg1.go b/src/udi/handlers/ttn/models/emuProfIILoRaCfg1/emuProfIILoRaCfg1.go index 0df6218..ba88c0a 100644 --- a/src/udi/handlers/ttn/models/emuProfIILoRaCfg1/emuProfIILoRaCfg1.go +++ b/src/udi/handlers/ttn/models/emuProfIILoRaCfg1/emuProfIILoRaCfg1.go @@ -177,83 +177,81 @@ type emuMessage1 struct { -func Parse(fPort int, decodedPayload []byte) (map[string]database.VariableType, error) { +func Parse(fPort int, decodedPayload []byte, variables *map[string]database.VariableType) error { //log.Printf("Parse input: %d, %s", fPort, decodedPayload) switch fPort { case 1: var emuMessage1 emuMessage1 err := json.Unmarshal(decodedPayload, &emuMessage1) if err != nil { - return nil, fmt.Errorf("Unable to parse payload, fPort %d, error %s", fPort, err) + return fmt.Errorf("Unable to parse payload, fPort %d, error %s", fPort, err) } - variables := make(map[string]database.VariableType) - variables["ActivePowerL1"] = database.VariableType { + (*variables)["ActivePowerL1"] = database.VariableType { Variable: "ActivePowerL1", Unit: emuMessage1.ActivePowerL1.Unit, Value: emuMessage1.ActivePowerL1.Value, } - variables["ActivePowerL2"] = database.VariableType { + (*variables)["ActivePowerL2"] = database.VariableType { Variable: "ActivePowerL2", Unit: emuMessage1.ActivePowerL2.Unit, Value: emuMessage1.ActivePowerL2.Value, } - variables["ActivePowerL3"] = database.VariableType { + (*variables)["ActivePowerL3"] = database.VariableType { Variable: "ActivePowerL3", Unit: emuMessage1.ActivePowerL3.Unit, Value: emuMessage1.ActivePowerL3.Value, } - variables["ActivePowerL123"] = database.VariableType { + (*variables)["ActivePowerL123"] = database.VariableType { Variable: "ActivePowerL123", Unit: emuMessage1.ActivePowerL123.Unit, Value: emuMessage1.ActivePowerL123.Value, } - variables["PowerfactorL1"] = database.VariableType { + (*variables)["PowerfactorL1"] = database.VariableType { Variable: "PowerfactorL1", Unit: emuMessage1.PowerfactorL1.Unit, Value: emuMessage1.PowerfactorL1.Value, } - variables["PowerfactorL2"] = database.VariableType { + (*variables)["PowerfactorL2"] = database.VariableType { Variable: "PowerfactorL2", Unit: emuMessage1.PowerfactorL2.Unit, Value: emuMessage1.PowerfactorL2.Value, } - variables["PowerfactorL3"] = database.VariableType { + (*variables)["PowerfactorL3"] = database.VariableType { Variable: "PowerfactorL3", Unit: emuMessage1.PowerfactorL3.Unit, Value: emuMessage1.PowerfactorL3.Value, } - return variables, nil + return nil case 2: var emuMessage2 emuMessage2 err := json.Unmarshal(decodedPayload, &emuMessage2) if err != nil { - return nil, fmt.Errorf("Unable to parse payload, fPort %d, error %s", fPort, err) + return fmt.Errorf("Unable to parse payload, fPort %d, error %s", fPort, err) } - variables := make(map[string]database.VariableType) - variables["ActiveEnergyExport"] = database.VariableType { + (*variables)["ActiveEnergyExport"] = database.VariableType { Variable: "ActiveEnergyExport", Unit: emuMessage2.ActiveEnergyExport.Unit, Value: emuMessage2.ActiveEnergyExport.Value, } - variables["ActiveEnergyImport"] = database.VariableType { + (*variables)["ActiveEnergyImport"] = database.VariableType { Variable: "ActiveEnergyImport", Unit: emuMessage2.ActiveEnergyImport.Unit, Value: emuMessage2.ActiveEnergyImport.Value, } - variables["ReactiveEnergyExport"] = database.VariableType { + (*variables)["ReactiveEnergyExport"] = database.VariableType { Variable: "ReactiveEnergyExport", Unit: emuMessage2.ReactiveEnergyExport.Unit, Value: emuMessage2.ReactiveEnergyExport.Value, } - variables["ReactiveEnergyImport"] = database.VariableType { + (*variables)["ReactiveEnergyImport"] = database.VariableType { Variable: "ReactiveEnergyImport", Unit: emuMessage2.ReactiveEnergyImport.Unit, Value: emuMessage2.ReactiveEnergyImport.Value, } - return variables, nil + return nil default: - return nil, fmt.Errorf("Unexpected fPort %d", fPort) + return fmt.Errorf("Unexpected fPort %d", fPort) } } diff --git a/src/udi/handlers/ttn/ttn.go b/src/udi/handlers/ttn/ttn.go index 6ffcdaa..55a93f7 100644 --- a/src/udi/handlers/ttn/ttn.go +++ b/src/udi/handlers/ttn/ttn.go @@ -1,13 +1,17 @@ package ttn -import "log" -import "fmt" -import "time" -import "encoding/json" -import "udi/config" -import "udi/handlers/handler" -import "udi/handlers/ttn/models/emuProfIILoRaCfg1" -import "udi/database" +import ( + "log" + "fmt" + "time" + "encoding/json" + "udi/config" + "udi/handlers/handler" + "udi/handlers/ttn/models/emuProfIILoRaCfg1" + "udi/handlers/ttn/models/draginoLdds75" + "udi/handlers/ttn/models/draginoLmds200" + "udi/database" +) var idSeq int = 0 @@ -134,24 +138,29 @@ func (self *TTNHandler) Handle(message handler.MessageT) { } measurement.Application = attributes.ApplicationId measurement.Device = attributes.DeviceId + measurement.Attributes["DeviceType"] = device.DeviceType.ModelIdentifier //log.Printf("DeviceLabel: %s, DeviceType: %s", device.Label, device.DeviceType.ModelIdentifier) - var parser func(int, []byte) (map[string]database.VariableType, error) + var parser func(int, []byte, *map[string]database.VariableType) error switch device.DeviceType.ModelIdentifier { case "emu-prof-ii-lora-cfg1": parser = emuProfIILoRaCfg1.Parse + case "dragino-ldds75": + parser = draginoLdds75.Parse + case "dragino-lmds200": + parser = draginoLmds200.Parse default: lost(fmt.Sprintf("No parser found for %s", device.DeviceType.ModelIdentifier), message) return } - variables, err3 := parser(uplinkMessage.UplinkMessage.FPort, uplinkMessage.UplinkMessage.DecodedPayload.Payload) + measurement.Values = make(map[string]database.VariableType) + err3 := parser(uplinkMessage.UplinkMessage.FPort, uplinkMessage.UplinkMessage.DecodedPayload.Payload, &(measurement.Values)) if err3 != nil { lost(fmt.Sprintf("Model parser failed: %s", err3), message) return } - measurement.Values = variables log.Printf("Prepared measurement item: %s", measurement) self.dbh.StoreMeasurement(&measurement) }