gyrogpsc/core/service.go

251 lines
5.8 KiB
Go

package core
import (
"context"
"errors"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"golang.org/x/sync/semaphore"
"sync"
"time"
)
type OpMode uint8
const (
STOPPED OpMode = iota
LIVE
RECORDING
REPLAY
)
type Service interface {
AllTrackings() ([]TrackingMetadata, error)
StartPipeline(cols ...CollectorType) (string, error)
StartRecord() (string, error)
StopRecord() (*TrackingMetadata, error)
StopAll() (*TrackingMetadata, error)
LoadTracking(trackingId uuid.UUID) (*Tracking, error)
DeleteTracking(trackingId uuid.UUID)
StartReplay()
PauseReplay()
StopReplay()
}
type Tracker interface {
Put(data SensorData)
Recorder
}
type Recorder interface {
SetRecording(s bool) (ok bool)
IsRecording() bool
}
type trackingService struct {
opMode OpMode
tracking *Tracking
pipeline *pipelineRecord
collectors []Collector
store Storer
publisher Publisher
config *Configuration
recSem *semaphore.Weighted
mu *sync.RWMutex
}
func TrackingService(c *Configuration, s Storer, p Publisher) *trackingService {
t := &Tracking{}
ts := &trackingService{
tracking: t,
opMode: STOPPED,
collectors: nil,
recSem: semaphore.NewWeighted(1),
mu: &sync.RWMutex{},
config: c,
store: s,
publisher: p,
}
// first initialize of tcp collector to to open tcp port
NewCollector(c, TCP)
return ts
}
func (t *trackingService) Put(data SensorData) {
if !t.IsRecording() {
return
}
t.mu.Lock()
t.tracking.Data = append(t.tracking.Data, data)
t.tracking.Size++
logrus.Traceln("raw data points: len->", len(t.tracking.Data))
t.mu.Unlock()
}
func (t *trackingService) SetRecording(s bool) (ok bool) {
if okay := t.recSem.TryAcquire(1); okay && s {
// if i want to turn on and can get semaphore then return success
return true
} else if !s && !okay {
// if i want to turn off and cant get semaphore, i can safely turn off by releasing semaphore and return success
t.recSem.Release(1)
return true
}
return false
}
func (t *trackingService) IsRecording() bool {
if t.recSem.TryAcquire(1) {
t.recSem.Release(1)
return false
}
return true
}
func (t *trackingService) StartPipeline(cols ...CollectorType) (string, error) {
logrus.Info("SERVICE: NEW PIPELINE")
if t.opMode == RECORDING {
txt := "trackingservice: please stop recording before resetting pipeline"
logrus.Warn(txt)
return "RECORDING", errors.New(txt)
}
if t.opMode == LIVE {
txt := "trackingservice: stop tracking running stream before creating new one"
logrus.Warnln(txt)
//t.StopAll()
//time.Sleep(1000 * time.Millisecond)
return "record already running since: " + t.tracking.TimeCreated.String(), errors.New(txt)
}
logrus.Debugln("new tracking:", cols)
t.opMode = LIVE
t.collectors = nil
var tcp, ser chan interface{}
for _, col := range cols {
c := NewCollector(t.config, col)
t.collectors = append(t.collectors, c)
if col == TCP {
tcp = c.OutChannel()
}
if col == SERIAL {
ser = c.OutChannel()
}
c.Collect()
}
t.safelyReplaceTracking(newTracking())
t.tracking.Collectors = cols
NewRecordPipeline(t.publisher, t, tcp, ser)
t.publisher.SetStreaming(true)
//time.Sleep(3 * time.Second)
return "LIVE", nil
}
func (t *trackingService) AllTrackings() ([]TrackingMetadata, error) {
logrus.Info("SERVICE: GET ALL TRACKINGS")
data, err := t.store.LoadAll()
return data, err
}
func (t *trackingService) StartRecord() (string, error) {
logrus.Info("SERVICE: START RECORD")
if t.opMode != LIVE {
if t.opMode == RECORDING {
txt := "trackingservice: already recording"
logrus.Warn(txt)
return "record already running since: " + t.tracking.TimeCreated.String(), errors.New(txt)
} else {
txt := "trackingservice: start collector pipeline to record data"
logrus.Warn(txt)
return "record already running since: " + t.tracking.TimeCreated.String(), errors.New(txt)
}
}
t.opMode = RECORDING
t.tracking.TimeCreated = time.Now()
t.SetRecording(true)
return "record started at: " + t.tracking.TimeCreated.String(), nil
}
func (t *trackingService) StopRecord() (*TrackingMetadata, error) {
logrus.Info("SERVICE: STOP RECORD")
if t.opMode != RECORDING {
txt := "trackingservice: couldn't stop. not recording"
logrus.Info(txt)
return nil, errors.New(txt)
}
t.SetRecording(false)
err := t.store.Save(*t.tracking)
if err != nil {
logrus.Error(err)
}
t.opMode = LIVE
//time.Sleep(20 * time.Millisecond)
tm := t.tracking.TrackingMetadata
t.safelyReplaceTracking(newTracking())
t.tracking.Collectors = tm.Collectors
return &tm, err
}
func (t *trackingService) StopAll() (*TrackingMetadata, error) {
logrus.Info("SERVICE: STOP ALL")
var tm *TrackingMetadata = nil
var err error
for _, e := range t.collectors {
e.Stop()
}
// let buffer run empty after collectors stopped
time.Sleep(time.Millisecond * 5)
t.publisher.SetStreaming(false)
if t.opMode == RECORDING {
logrus.Info("trackingservice: gracefully stop recording ")
tm, err = t.StopRecord()
}
t.opMode = STOPPED
return tm, err
}
func (t *trackingService) LoadTracking(trackingId uuid.UUID) (*Tracking, error) {
if !(t.opMode == REPLAY || t.opMode == STOPPED) {
t.StopAll()
}
logrus.Info("LOAD TRACKING from database")
tracking, err := t.store.Load(trackingId)
if err != nil {
return nil, err
}
t.safelyReplaceTracking(*tracking)
NewReplayPipeline(t.publisher, t.tracking)
t.publisher.SetStreaming(true)
t.opMode = REPLAY
return t.tracking, nil
}
func (t *trackingService) DeleteTracking(trackingId uuid.UUID) {
panic("implement me")
}
func (t *trackingService) StartReplay() {
panic("implement me")
}
func (t *trackingService) PauseReplay() {
panic("implement me")
}
func (t *trackingService) StopReplay() {
panic("implement me")
}
func (t *trackingService) safelyReplaceTracking(tr Tracking) {
t.recSem.Acquire(context.Background(), 1)
*t.tracking = tr
t.recSem.Release(1)
}