Compare commits

..

3 Commits

Author SHA1 Message Date
42ff8b51ed float fix 3
All checks were successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2026-03-06 21:35:32 +01:00
f63c22912a float fix 2
All checks were successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2026-03-06 20:57:32 +01:00
c37420a993 float fix 1
All checks were successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2026-03-06 20:51:50 +01:00
4 changed files with 282 additions and 277 deletions

View File

@@ -105,6 +105,7 @@
"devicePart": "3", "devicePart": "3",
"valueFrom": "payload", "valueFrom": "payload",
"valuePart": "1", "valuePart": "1",
"valueType": "float",
"unitFrom": "payload", "unitFrom": "payload",
"unitPart": "3" "unitPart": "3"
} }

View File

@@ -1,81 +1,75 @@
package dt1t package dt1t
import ( import (
"log" "log"
"fmt" "strconv"
"time" "time"
"strconv" "udi/config"
"udi/handlers/handler" "udi/database"
"udi/database" "udi/handlers/handler"
"udi/config"
) )
type Dt1tHandler struct { type Dt1tHandler struct {
handler.CommonHandler handler.CommonHandler
ready bool ready bool
label string label string
dbh *database.DatabaseHandle dbh *database.DatabaseHandle
application string application string
device string device string
} }
func New(id string, config config.HandlerConfigT) handler.Handler { func New(id string, config config.HandlerConfigT) handler.Handler {
t := &Dt1tHandler { t := &Dt1tHandler{}
}
if config.Attributes["Application"] == "" { if config.Attributes["Application"] == "" {
log.Println("Error: application not configured") log.Println("Error: application not configured")
return t return t
} }
t.application = config.Attributes["Application"] t.application = config.Attributes["Application"]
if config.Attributes["Device"] == "" { if config.Attributes["Device"] == "" {
log.Println("Error: device not configured") log.Println("Error: device not configured")
return t return t
} }
t.device = config.Attributes["Device"] t.device = config.Attributes["Device"]
t.Id = id t.Id = id
t.dbh = database.NewDatabaseHandle() t.dbh = database.NewDatabaseHandle()
log.Printf("Handler DT1T %d initialized", id) log.Printf("Handler DT1T %d initialized", id)
t.ready = true t.ready = true
return t return t
} }
func (self *Dt1tHandler) Handle(message handler.MessageT) { func (self *Dt1tHandler) Handle(message handler.MessageT) {
if ! self.ready { if !self.ready {
self.Lost("Handler is not marked as ready", nil, message) self.Lost("Handler is not marked as ready", nil, message)
return return
} }
// log.Printf("Handler DT1T %d processing %s -> %s", self.id, message.Topic, message.Payload) // log.Printf("Handler DT1T %d processing %s -> %s", self.id, message.Topic, message.Payload)
temperature, err := strconv.Atoi(message.Payload) temperature, err := strconv.Atoi(message.Payload)
if err != nil { if err != nil {
self.Lost("Invalid raw value", err, message) self.Lost("Invalid raw value", err, message)
return return
} }
if temperature & 0x8000 != 0{ if temperature&0x8000 != 0 {
temperature = ((temperature - 1) ^ 0xffff) * -1 temperature = ((temperature - 1) ^ 0xffff) * -1
} }
temperatureF := float32(temperature) / 10.0 temperatureF := float32(temperature) / 10.0
var measurement database.Measurement var measurement database.Measurement
measurement.Time = time.Now() measurement.Time = time.Now()
measurement.Application = self.application measurement.Application = self.application
measurement.Device = self.device measurement.Device = self.device
var variable database.VariableType var variable database.VariableType
variable.Label = "Temperature" variable.Label = "Temperature"
variable.Variable = "" variable.Variable = ""
variable.Unit = "°C" variable.Unit = "°C"
variable.Value = fmt.Sprintf("%f", temperatureF) variable.Value = temperatureF
measurement.Values = make(map[string]database.VariableType) measurement.Values = make(map[string]database.VariableType)
measurement.Values["Value"] = variable measurement.Values["Value"] = variable
// log.Printf("Prepared measurement item: %s", measurement) // log.Printf("Prepared measurement item: %s", measurement)
self.dbh.StoreMeasurement(&measurement) self.dbh.StoreMeasurement(&measurement)
self.S() self.S()
} }

View File

@@ -1,79 +1,74 @@
package prepared package prepared
import ( import (
"time" "encoding/json"
"log" "log"
"encoding/json" "time"
"udi/config" "udi/config"
"udi/handlers/handler" "udi/database"
"udi/database" "udi/handlers/handler"
) )
type PreparedHandler struct { type PreparedHandler struct {
handler.CommonHandler handler.CommonHandler
dbh *database.DatabaseHandle dbh *database.DatabaseHandle
} }
type endpoint_t struct { type endpoint_t struct {
Label string `json:"label"` Label string `json:"label"`
Variable string `json:"variable"` Variable string `json:"variable"`
Value string `json:"value"` Value interface{} `json:"value"`
Unit string `json:"unit"` Unit string `json:"unit"`
Status string `json:"status"` Status string `json:"status"`
} }
type observation_t struct { type observation_t struct {
Device string `json:"device"` Device string `json:"device"`
Label string `json:"label"` Label string `json:"label"`
Variables map[string]endpoint_t `json:"variables"` Variables map[string]endpoint_t `json:"variables"`
} }
func New(id string, config config.HandlerConfigT) handler.Handler { func New(id string, config config.HandlerConfigT) handler.Handler {
t := &PreparedHandler { t := &PreparedHandler{}
} t.Id = id
t.Id = id t.dbh = database.NewDatabaseHandle()
t.dbh = database.NewDatabaseHandle() log.Printf("Handler Prepared %d initialized", id)
log.Printf("Handler Prepared %d initialized", id) return t
return t
} }
func (self *PreparedHandler) Handle(message handler.MessageT) { func (self *PreparedHandler) Handle(message handler.MessageT) {
log.Printf("Handler Prepared %d processing %s -> %s", self.Id, message.Topic, message.Payload) log.Printf("Handler Prepared %d processing %s -> %s", self.Id, message.Topic, message.Payload)
var observation observation_t var observation observation_t
err := json.Unmarshal([]byte(message.Payload), &observation) err := json.Unmarshal([]byte(message.Payload), &observation)
if err != nil { if err != nil {
self.Lost("Unable to parse payload into Observation struct", err, message) self.Lost("Unable to parse payload into Observation struct", err, message)
return return
} }
var measurement database.Measurement var measurement database.Measurement
measurement.Time = time.Now() measurement.Time = time.Now()
measurement.Application = self.Id measurement.Application = self.Id
measurement.Device = observation.Device measurement.Device = observation.Device
measurement.Attributes = map[string]interface{} { measurement.Attributes = map[string]interface{}{
"Label": observation.Label, "Label": observation.Label,
} }
measurement.Values = make(map[string]database.VariableType) measurement.Values = make(map[string]database.VariableType)
for k, v := range observation.Variables { for k, v := range observation.Variables {
measurement.Values[k] = database.VariableType { measurement.Values[k] = database.VariableType{
Label: v.Label, Label: v.Label,
Variable: v.Variable, Variable: v.Variable,
Unit: v.Unit, Unit: v.Unit,
Value: v.Value, Value: v.Value,
Status: v.Status, Status: v.Status,
} }
} }
log.Printf("Prepared measurement item: %s", measurement) log.Printf("Prepared measurement item: %s", measurement)
self.dbh.StoreMeasurement(&measurement) self.dbh.StoreMeasurement(&measurement)
self.S() self.S()
} }

View File

@@ -1,23 +1,22 @@
package sver package sver
import ( import (
"time" "log"
"strconv" "regexp"
"strings" "strconv"
"regexp" "strings"
"log" "time"
"udi/config" "udi/config"
"udi/handlers/handler" "udi/database"
"udi/database" "udi/handlers/handler"
) )
type SingleValueExtractorRegexHandler struct { type SingleValueExtractorRegexHandler struct {
handler.CommonHandler handler.CommonHandler
ready bool ready bool
config localConfig config localConfig
payloadRegex *regexp.Regexp payloadRegex *regexp.Regexp
dbh *database.DatabaseHandle dbh *database.DatabaseHandle
} }
const TOPIC_SEL = "topic" const TOPIC_SEL = "topic"
@@ -26,175 +25,191 @@ const PAYLOAD_FULL_SEL = "payload-full"
const CONSTANT_SEL = "constant" const CONSTANT_SEL = "constant"
type localConfig struct { type localConfig struct {
application string application string
deviceFrom string deviceFrom string
devicePart int devicePart int
device string device string
valueFrom string valueFrom string
valuePart int valuePart int
unitFrom string valueType string
unitPart int unitFrom string
unit string unitPart int
unit string
} }
func New(id string, config config.HandlerConfigT) handler.Handler { func New(id string, config config.HandlerConfigT) handler.Handler {
t := &SingleValueExtractorRegexHandler { t := &SingleValueExtractorRegexHandler{
ready: false, ready: false,
} }
var localConfig localConfig var localConfig localConfig
if config.Attributes["application"] == "" { if config.Attributes["application"] == "" {
log.Println("Error: application not configured") log.Println("Error: application not configured")
return t return t
} }
localConfig.application = config.Attributes["application"] localConfig.application = config.Attributes["application"]
payloadRegex := config.Attributes["payloadRegex"] payloadRegex := config.Attributes["payloadRegex"]
if payloadRegex != "" { if payloadRegex != "" {
t.payloadRegex = regexp.MustCompile(payloadRegex) t.payloadRegex = regexp.MustCompile(payloadRegex)
} else { } else {
t.payloadRegex = nil t.payloadRegex = nil
} }
if config.Attributes["deviceFrom"] != TOPIC_SEL && config.Attributes["deviceFrom"] != PAYLOAD_SEL && config.Attributes["deviceFrom"] != CONSTANT_SEL { if config.Attributes["deviceFrom"] != TOPIC_SEL && config.Attributes["deviceFrom"] != PAYLOAD_SEL && config.Attributes["deviceFrom"] != CONSTANT_SEL {
log.Printf("Error: invalid value %s for deviceFrom", config.Attributes["deviceFrom"]) log.Printf("Error: invalid value %s for deviceFrom", config.Attributes["deviceFrom"])
return t return t
} }
localConfig.deviceFrom = config.Attributes["deviceFrom"] localConfig.deviceFrom = config.Attributes["deviceFrom"]
devicePart, err1 := strconv.Atoi(config.Attributes["devicePart"]) devicePart, err1 := strconv.Atoi(config.Attributes["devicePart"])
if err1 != nil { if err1 != nil {
log.Printf("Error: unable to convert devicePart to number: %s", err1) log.Printf("Error: unable to convert devicePart to number: %s", err1)
return t return t
} }
localConfig.devicePart = devicePart localConfig.devicePart = devicePart
// empty device is valid // empty device is valid
localConfig.device = config.Attributes["device"] localConfig.device = config.Attributes["device"]
if config.Attributes["valueFrom"] != PAYLOAD_SEL && config.Attributes["valueFrom"] != PAYLOAD_FULL_SEL { if config.Attributes["valueFrom"] != PAYLOAD_SEL && config.Attributes["valueFrom"] != PAYLOAD_FULL_SEL {
log.Printf("Error: invalid value %s for valueFrom", config.Attributes["valueFrom"]) log.Printf("Error: invalid value %s for valueFrom", config.Attributes["valueFrom"])
return t return t
} }
localConfig.valueFrom = config.Attributes["valueFrom"] localConfig.valueFrom = config.Attributes["valueFrom"]
if config.Attributes["valueFrom"] == PAYLOAD_SEL { if config.Attributes["valueFrom"] == PAYLOAD_SEL {
valuePart, err2 := strconv.Atoi(config.Attributes["valuePart"]) valuePart, err2 := strconv.Atoi(config.Attributes["valuePart"])
if err2 != nil { if err2 != nil {
log.Printf("Error: unable to convert valuePart to number: %s", err2) log.Printf("Error: unable to convert valuePart to number: %s", err2)
return t return t
} }
localConfig.valuePart = valuePart localConfig.valuePart = valuePart
} }
if config.Attributes["unitFrom"] != PAYLOAD_SEL && config.Attributes["unitFrom"] != CONSTANT_SEL { if config.Attributes["valueType"] != "float" && config.Attributes["valueType"] != "string" {
log.Printf("Error: invalid value %s for unitFrom", config.Attributes["unitFrom"]) log.Printf("Error: invalid value %s for valueType", config.Attributes["valueType"])
return t return t
} }
localConfig.unitFrom = config.Attributes["unitFrom"] localConfig.valueType = config.Attributes["valueType"]
if config.Attributes["unitFrom"] == PAYLOAD_SEL { if config.Attributes["unitFrom"] != PAYLOAD_SEL && config.Attributes["unitFrom"] != CONSTANT_SEL {
unitPart, err3 := strconv.Atoi(config.Attributes["unitPart"]) log.Printf("Error: invalid value %s for unitFrom", config.Attributes["unitFrom"])
if err3 != nil { return t
log.Printf("Error: unable to convert unitPart to number: %s", err3) }
return t localConfig.unitFrom = config.Attributes["unitFrom"]
}
localConfig.unitPart = unitPart
}
// empty unit is valid if config.Attributes["unitFrom"] == PAYLOAD_SEL {
localConfig.unit = config.Attributes["unit"] unitPart, err3 := strconv.Atoi(config.Attributes["unitPart"])
if err3 != nil {
log.Printf("Error: unable to convert unitPart to number: %s", err3)
return t
}
localConfig.unitPart = unitPart
}
t.config = localConfig // empty unit is valid
localConfig.unit = config.Attributes["unit"]
t.Id = id t.config = localConfig
t.ready = true
t.dbh = database.NewDatabaseHandle() t.Id = id
log.Printf("Handler SVER %d initialized", id) t.ready = true
return t t.dbh = database.NewDatabaseHandle()
log.Printf("Handler SVER %d initialized", id)
return t
} }
func (self *SingleValueExtractorRegexHandler) Handle(message handler.MessageT) { func (self *SingleValueExtractorRegexHandler) Handle(message handler.MessageT) {
if ! self.ready { if !self.ready {
self.Lost("Handler is not marked as ready", nil, message) self.Lost("Handler is not marked as ready", nil, message)
return return
} }
//log.Printf("Handler SingleValueExtractor %d processing %s -> %s", self.id, message.Topic, message.Payload) //log.Printf("Handler SingleValueExtractor %d processing %s -> %s", self.id, message.Topic, message.Payload)
var measurement database.Measurement var measurement database.Measurement
measurement.Time = time.Now() measurement.Time = time.Now()
measurement.Application = self.config.application measurement.Application = self.config.application
subTopics := strings.Split(message.Topic, "/") subTopics := strings.Split(message.Topic, "/")
//log.Printf("Subtopics: %s", strings.Join(subTopics, ", ")) //log.Printf("Subtopics: %s", strings.Join(subTopics, ", "))
var payloadMatches []string var payloadMatches []string
if self.payloadRegex != nil { if self.payloadRegex != nil {
payloadMatches = self.payloadRegex.FindStringSubmatch(message.Payload) payloadMatches = self.payloadRegex.FindStringSubmatch(message.Payload)
//log.Printf("Matches: %s", strings.Join(payloadMatches, ", ")) //log.Printf("Matches: %s", strings.Join(payloadMatches, ", "))
} }
switch self.config.deviceFrom { switch self.config.deviceFrom {
case TOPIC_SEL: case TOPIC_SEL:
if self.config.devicePart >= len(subTopics) { if self.config.devicePart >= len(subTopics) {
self.Lost("devicePart out of range", nil, message) self.Lost("devicePart out of range", nil, message)
return return
} }
measurement.Device = subTopics[self.config.devicePart] measurement.Device = subTopics[self.config.devicePart]
case PAYLOAD_SEL: case PAYLOAD_SEL:
if self.payloadRegex == nil { if self.payloadRegex == nil {
self.Lost("no payloadRegex defined, devicePart can't be used", nil, message) self.Lost("no payloadRegex defined, devicePart can't be used", nil, message)
return return
} }
if self.config.devicePart >= len(payloadMatches) { if self.config.devicePart >= len(payloadMatches) {
self.Lost("devicePart out of range", nil, message) self.Lost("devicePart out of range", nil, message)
return return
} }
measurement.Device = payloadMatches[self.config.devicePart] measurement.Device = payloadMatches[self.config.devicePart]
case CONSTANT_SEL: case CONSTANT_SEL:
measurement.Device = self.config.device measurement.Device = self.config.device
} }
measurement.Values = make(map[string]database.VariableType) measurement.Values = make(map[string]database.VariableType)
var variable database.VariableType var variable database.VariableType
variable.Label = "" variable.Label = ""
variable.Variable = "" variable.Variable = ""
switch self.config.valueFrom { var value string
case PAYLOAD_SEL: switch self.config.valueFrom {
if self.payloadRegex == nil { case PAYLOAD_SEL:
self.Lost("no payloadRegex defined, valuePart can't be used", nil, message) if self.payloadRegex == nil {
return self.Lost("no payloadRegex defined, valuePart can't be used", nil, message)
} return
if self.config.valuePart >= len(payloadMatches) { }
self.Lost("valuePart out of range", nil, message) if self.config.valuePart >= len(payloadMatches) {
return self.Lost("valuePart out of range", nil, message)
} return
variable.Value = payloadMatches[self.config.valuePart] }
case PAYLOAD_FULL_SEL: value = payloadMatches[self.config.valuePart]
variable.Value = message.Payload case PAYLOAD_FULL_SEL:
} value = message.Payload
}
if self.config.valueType == "float" {
fValue, err := strconv.ParseFloat(value, 64)
if err != nil {
self.Lost("Unable to convert value to float", err, message)
return
}
variable.Value = fValue
} else {
variable.Value = value
}
switch self.config.unitFrom { switch self.config.unitFrom {
case PAYLOAD_SEL: case PAYLOAD_SEL:
if self.payloadRegex == nil { if self.payloadRegex == nil {
self.Lost("no payloadRegex defined, unitPart can't be used", nil, message) self.Lost("no payloadRegex defined, unitPart can't be used", nil, message)
return return
} }
if self.config.unitPart >= len(payloadMatches) { if self.config.unitPart >= len(payloadMatches) {
self.Lost("unitPart out of range", nil, message) self.Lost("unitPart out of range", nil, message)
return return
} }
variable.Unit = payloadMatches[self.config.unitPart] variable.Unit = payloadMatches[self.config.unitPart]
case CONSTANT_SEL: case CONSTANT_SEL:
variable.Unit = self.config.unit variable.Unit = self.config.unit
} }
measurement.Values["Value"] = variable measurement.Values["Value"] = variable
//log.Printf("Prepared measurement item: %s", measurement) //log.Printf("Prepared measurement item: %s", measurement)
self.dbh.StoreMeasurement(&measurement) self.dbh.StoreMeasurement(&measurement)
self.S() self.S()
} }