133 lines
2.9 KiB
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
|
|
}
|