float fix 1
All checks were successful
ci/woodpecker/tag/woodpecker Pipeline was successful

This commit is contained in:
2026-03-06 20:51:50 +01:00
parent ac0c417a48
commit c37420a993
2 changed files with 176 additions and 160 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,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"]
if payloadRegex != "" {
t.payloadRegex = regexp.MustCompile(payloadRegex)
} else {
t.payloadRegex = nil
}
if config.Attributes["deviceFrom"] != TOPIC_SEL && config.Attributes["deviceFrom"] != PAYLOAD_SEL && config.Attributes["deviceFrom"] != CONSTANT_SEL { payloadRegex := config.Attributes["payloadRegex"]
log.Printf("Error: invalid value %s for deviceFrom", config.Attributes["deviceFrom"]) if payloadRegex != "" {
return t t.payloadRegex = regexp.MustCompile(payloadRegex)
} } else {
localConfig.deviceFrom = config.Attributes["deviceFrom"] t.payloadRegex = nil
}
devicePart, err1 := strconv.Atoi(config.Attributes["devicePart"]) if config.Attributes["deviceFrom"] != TOPIC_SEL && config.Attributes["deviceFrom"] != PAYLOAD_SEL && config.Attributes["deviceFrom"] != CONSTANT_SEL {
if err1 != nil { log.Printf("Error: invalid value %s for deviceFrom", config.Attributes["deviceFrom"])
log.Printf("Error: unable to convert devicePart to number: %s", err1) return t
return t }
} localConfig.deviceFrom = config.Attributes["deviceFrom"]
localConfig.devicePart = devicePart
// empty device is valid devicePart, err1 := strconv.Atoi(config.Attributes["devicePart"])
localConfig.device = config.Attributes["device"] if err1 != nil {
log.Printf("Error: unable to convert devicePart to number: %s", err1)
return t
}
localConfig.devicePart = devicePart
if config.Attributes["valueFrom"] != PAYLOAD_SEL && config.Attributes["valueFrom"] != PAYLOAD_FULL_SEL { // empty device is valid
log.Printf("Error: invalid value %s for valueFrom", config.Attributes["valueFrom"]) localConfig.device = config.Attributes["device"]
return t
}
localConfig.valueFrom = config.Attributes["valueFrom"]
if config.Attributes["valueFrom"] == PAYLOAD_SEL { if config.Attributes["valueFrom"] != PAYLOAD_SEL && config.Attributes["valueFrom"] != PAYLOAD_FULL_SEL {
valuePart, err2 := strconv.Atoi(config.Attributes["valuePart"]) log.Printf("Error: invalid value %s for valueFrom", config.Attributes["valueFrom"])
if err2 != nil { return t
log.Printf("Error: unable to convert valuePart to number: %s", err2) }
return t localConfig.valueFrom = config.Attributes["valueFrom"]
}
localConfig.valuePart = valuePart
}
if config.Attributes["unitFrom"] != PAYLOAD_SEL && config.Attributes["unitFrom"] != CONSTANT_SEL { if config.Attributes["valueFrom"] == PAYLOAD_SEL {
log.Printf("Error: invalid value %s for unitFrom", config.Attributes["unitFrom"]) valuePart, err2 := strconv.Atoi(config.Attributes["valuePart"])
return t if err2 != nil {
} log.Printf("Error: unable to convert valuePart to number: %s", err2)
localConfig.unitFrom = config.Attributes["unitFrom"] return t
}
localConfig.valuePart = valuePart
}
if config.Attributes["unitFrom"] == PAYLOAD_SEL { if config.Attributes["valueType"] != "float" && config.Attributes["valueType"] != "string" {
unitPart, err3 := strconv.Atoi(config.Attributes["unitPart"]) log.Printf("Error: invalid value %s for valueType", config.Attributes["valueType"])
if err3 != nil { return t
log.Printf("Error: unable to convert unitPart to number: %s", err3) }
return t localConfig.valueType = config.Attributes["valueType"]
}
localConfig.unitPart = unitPart
}
// empty unit is valid if config.Attributes["unitFrom"] != PAYLOAD_SEL && config.Attributes["unitFrom"] != CONSTANT_SEL {
localConfig.unit = config.Attributes["unit"] log.Printf("Error: invalid value %s for unitFrom", config.Attributes["unitFrom"])
return t
}
localConfig.unitFrom = config.Attributes["unitFrom"]
t.config = localConfig if config.Attributes["unitFrom"] == PAYLOAD_SEL {
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.Id = id // empty unit is valid
t.ready = true localConfig.unit = config.Attributes["unit"]
t.dbh = database.NewDatabaseHandle()
log.Printf("Handler SVER %d initialized", id) t.config = localConfig
return t
t.Id = id
t.ready = true
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, "/")
//log.Printf("Subtopics: %s", strings.Join(subTopics, ", "))
var payloadMatches []string subTopics := strings.Split(message.Topic, "/")
if self.payloadRegex != nil { //log.Printf("Subtopics: %s", strings.Join(subTopics, ", "))
payloadMatches = self.payloadRegex.FindStringSubmatch(message.Payload)
//log.Printf("Matches: %s", strings.Join(payloadMatches, ", "))
}
switch self.config.deviceFrom { var payloadMatches []string
case TOPIC_SEL: if self.payloadRegex != nil {
if self.config.devicePart >= len(subTopics) { payloadMatches = self.payloadRegex.FindStringSubmatch(message.Payload)
self.Lost("devicePart out of range", nil, message) //log.Printf("Matches: %s", strings.Join(payloadMatches, ", "))
return }
}
measurement.Device = subTopics[self.config.devicePart]
case PAYLOAD_SEL:
if self.payloadRegex == nil {
self.Lost("no payloadRegex defined, devicePart can't be used", nil, message)
return
}
if self.config.devicePart >= len(payloadMatches) {
self.Lost("devicePart out of range", nil, message)
return
}
measurement.Device = payloadMatches[self.config.devicePart]
case CONSTANT_SEL:
measurement.Device = self.config.device
}
measurement.Values = make(map[string]database.VariableType) switch self.config.deviceFrom {
var variable database.VariableType case TOPIC_SEL:
variable.Label = "" if self.config.devicePart >= len(subTopics) {
variable.Variable = "" self.Lost("devicePart out of range", nil, message)
return
}
measurement.Device = subTopics[self.config.devicePart]
case PAYLOAD_SEL:
if self.payloadRegex == nil {
self.Lost("no payloadRegex defined, devicePart can't be used", nil, message)
return
}
if self.config.devicePart >= len(payloadMatches) {
self.Lost("devicePart out of range", nil, message)
return
}
measurement.Device = payloadMatches[self.config.devicePart]
case CONSTANT_SEL:
measurement.Device = self.config.device
}
switch self.config.valueFrom { measurement.Values = make(map[string]database.VariableType)
case PAYLOAD_SEL: var variable database.VariableType
if self.payloadRegex == nil { variable.Label = ""
self.Lost("no payloadRegex defined, valuePart can't be used", nil, message) variable.Variable = ""
return
}
if self.config.valuePart >= len(payloadMatches) {
self.Lost("valuePart out of range", nil, message)
return
}
variable.Value = payloadMatches[self.config.valuePart]
case PAYLOAD_FULL_SEL:
variable.Value = message.Payload
}
switch self.config.unitFrom { var value string
case PAYLOAD_SEL: switch self.config.valueFrom {
if self.payloadRegex == nil { case PAYLOAD_SEL:
self.Lost("no payloadRegex defined, unitPart 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.unitPart >= len(payloadMatches) { }
self.Lost("unitPart out of range", nil, message) if self.config.valuePart >= len(payloadMatches) {
return self.Lost("valuePart out of range", nil, message)
} return
variable.Unit = payloadMatches[self.config.unitPart] }
case CONSTANT_SEL: value = payloadMatches[self.config.valuePart]
variable.Unit = self.config.unit 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
}
measurement.Values["Value"] = variable switch self.config.unitFrom {
case PAYLOAD_SEL:
if self.payloadRegex == nil {
self.Lost("no payloadRegex defined, unitPart can't be used", nil, message)
return
}
if self.config.unitPart >= len(payloadMatches) {
self.Lost("unitPart out of range", nil, message)
return
}
variable.Unit = payloadMatches[self.config.unitPart]
case CONSTANT_SEL:
variable.Unit = self.config.unit
}
//log.Printf("Prepared measurement item: %s", measurement) measurement.Values["Value"] = variable
self.dbh.StoreMeasurement(&measurement)
self.S() //log.Printf("Prepared measurement item: %s", measurement)
self.dbh.StoreMeasurement(&measurement)
self.S()
} }