src.dualinventive.com/go/influxdb-logger/internal/influxdb/influx.go

133 lines
2.9 KiB
Go

package influxdb
import (
"context"
"sync"
"time"
client "github.com/influxdata/influxdb1-client/v2"
"github.com/sirupsen/logrus"
"src.dualinventive.com/go/dinet/rpc"
"src.dualinventive.com/go/influxdb-logger/internal/device"
"src.dualinventive.com/go/lib/kv"
)
const (
measurementEvent = "event"
measurementDevice = "device"
measurementDeviceError = "device_error"
)
// Influx writes measurements to an influx database
type Influx struct {
c chan *rpc.Msg
devCache *device.Cache
writer Writer
}
// New creates a new Influx connection
func New(uri, username, password, db string, bufferSize int, redis kv.KV, cacheSize int,
cacheExpiry time.Duration) (*Influx, error) {
c, err := client.NewHTTPClient(client.HTTPConfig{Addr: uri, Username: username, Password: password})
if err != nil {
return nil, err
}
return &Influx{
c: make(chan *rpc.Msg),
devCache: device.New(redis, cacheSize, cacheExpiry),
writer: NewWriter(c, db, bufferSize),
}, nil
}
// Send sends a message to influx. It is processed internally by another go routine.
func (i *Influx) Send(m *rpc.Msg) error {
i.c <- m
return nil
}
// Process processes all incomming write messages. This method blocks until the context is done.
func (i *Influx) Process(ctx context.Context, writeInterval time.Duration) {
var wg sync.WaitGroup
defer wg.Wait()
wg.Add(1)
go func() {
defer wg.Done()
i.writer.Dispatch(ctx, writeInterval)
}()
for {
select {
case <-ctx.Done():
return
case m := <-i.c:
if err := i.process(m); err != nil {
logrus.Error("Unable to process RPC message:", err)
}
}
}
}
// process processes a single rpc message
func (i *Influx) process(m *rpc.Msg) error {
errCode := rpc.Ok
if m.Error != nil {
errCode = m.Error.Code
}
// Drop request messages
if m.Type == rpc.MsgTypeRequest {
return nil
}
var deviceType string
d, err := i.devCache.Device(m.DeviceUID)
if err != nil {
logrus.Warnf("cannot get device '%s' from cache: %v", m.DeviceUID, err)
}
if d != nil {
deviceType = d.Type
}
rpcPt, err := client.NewPoint(
measurementEvent,
map[string]string{
"device:uid": m.DeviceUID,
"device:group": m.DeviceUID[0:2],
"device:type": deviceType,
"class:method": string(m.ClassMethod),
"type": string(m.Type),
},
map[string]interface{}{
"errno": uint32(errCode),
},
m.Time.ToTime(),
)
if err != nil {
logrus.Errorf("error while creating new samplepoint: %v", err)
return err
}
i.writer.Add(rpcPt)
// Ignore error messages
if m.Error != nil {
return nil
}
return i.routeMessage(m)
}
func (i *Influx) routeMessage(m *rpc.Msg) error {
// Route the right class methods
switch m.ClassMethod {
case rpc.ClassMethodDeviceData:
return i.processDeviceData(m)
case rpc.ClassMethodSensorData:
return i.processResultValueItems(m, rpc.ClassSensor)
case rpc.ClassMethodNotifyData:
return i.processResultValueItems(m, rpc.ClassNotify)
}
return nil
}