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 }