src.dualinventive.com/go/cp3000-interface/internal/statusupdate/status_decoder.go

82 lines
2.4 KiB
Go

package statusupdate
import (
"encoding/csv"
"errors"
"strings"
"sync"
"src.dualinventive.com/go/lib/dilog"
)
// StatusDecoder decodes cp3000 status messages
type StatusDecoder struct {
mu sync.RWMutex
prevUpdateState *StatusUpdate
resetFields StatUpdateField
logger dilog.Logger
}
// NewStatusDecoder creates a new status decoder. The initial parameter is already set.
func NewStatusDecoder(logger dilog.Logger, fields StatUpdateField) *StatusDecoder {
return &StatusDecoder{
prevUpdateState: &StatusUpdate{
Fields: fields,
},
resetFields: fields,
logger: logger,
}
}
// Decode decodes the cp3000 status message
func (d *StatusDecoder) Decode(args []string) error {
r := csv.NewReader(strings.NewReader(args[1]))
r.Comma = ','
r.LazyQuotes = true
record, err := r.Read()
if err != nil {
return err
}
if len(record) != 22 {
return errors.New("wrong record count for statusupdate")
}
// This decoder is based on the original TCP-server implemetation,
// a status message can contains all fields but some may miss.
// no errors are checked since the checksum is complete and we know
// we have 22 fields at this point, this MUST be a generic status update
// ---- SAME AS LEGACY ---- >:-D
s := &StatusUpdate{}
// IMPORTANT the order here is important
records := decodeState(d.logger, s, StatUpdateAll, record)
records = decodeWCPUState(d.logger, s, StatUpdateAll, records)
records = decodeAutocal(d.logger, s, StatUpdateAll, records)
records = decodeRMS(d.logger, s, StatUpdateAll, records)
records = decodeBA(d.logger, s, StatUpdateAll, records)
records = decodeBatSelected(d.logger, s, StatUpdateAll, records)
records = decodeBatt1Level(d.logger, s, StatUpdateAll, records)
records = decodeBatt2Level(d.logger, s, StatUpdateAll, records)
records = decodeTempOnBoard(d.logger, s, StatUpdateAll, records)
records = decodeTempNTC(d.logger, s, StatUpdateAll, records)
records = decodeGSM(d.logger, s, StatUpdateAll, records)
decodeGPS(d.logger, s, StatUpdateAll, records)
// The GPS is updated every 30 minutes we want to send all sensor
// every once in a while, so we abuse the GPS-time :-)
if d.prevUpdateState.GPSTime != s.GPSTime {
s.Fields = d.resetFields
}
d.mu.Lock()
d.prevUpdateState = s
d.mu.Unlock()
return nil
}
// StatusUpdate returns the last known status update
func (d *StatusDecoder) StatusUpdate() *StatusUpdate {
d.mu.RLock()
defer d.mu.RUnlock()
return d.prevUpdateState
}