dataRT/data/postgres/measurement.go

200 lines
4.1 KiB
Go

package postgres
import (
"context"
"database/sql/driver"
"encoding/json"
"errors"
"sync/atomic"
)
const (
tbmeasurement string = "public.measurement"
)
const (
ChannelCPrefix string = "TM"
ChannelIPrefix string = "TS"
ChannelP string = "P"
ChannelQ string = "Q"
ChannelS string = "S"
ChannelPF string = "PF"
ChannelF string = "F"
ChannelDF string = "deltaF"
ChannelUPrefix string = "U"
)
type dataSource struct {
Type int `json:"type"`
Addr any `json:"io_address"`
}
func (ds *dataSource) Scan(value any) error {
if value == nil {
return nil
}
bytes, ok := value.([]byte)
if !ok {
return errors.New("type assertion to []byte failed")
}
return json.Unmarshal(bytes, ds)
}
func (ds *dataSource) Value() (driver.Value, error) {
return json.Marshal(ds)
}
type measurement struct {
ID int64 `gorm:"colunmn:id"`
Tag string `gorm:"column:tag"`
Size int `gorm:"column:size"`
DataSource *dataSource `gorm:"column:data_source;type:jsonb"`
// mapping TODO
}
type ChannelSize struct {
Station string
Device string
Channel string
Size int
}
// channel is original
var SSU2ChannelSizes atomic.Value
func init() {
SSU2ChannelSizes.Store(map[string][]ChannelSize{})
}
func LoadSSU2ChannelSizes() map[string][]ChannelSize {
v := SSU2ChannelSizes.Load()
if v == nil {
return nil
}
return v.(map[string][]ChannelSize)
}
func StoreSSU2ChannelSizes(m map[string][]ChannelSize) {
SSU2ChannelSizes.Store(m)
}
func GetSSU2ChannelSizesCopy() map[string][]ChannelSize {
src := LoadSSU2ChannelSizes()
if src == nil {
return nil
}
out := make(map[string][]ChannelSize, len(src))
for k, v := range src {
cp := make([]ChannelSize, len(v))
copy(cp, v)
out[k] = cp
}
return out
}
func GetSSU2ChannelSizesFor(ssu string) []ChannelSize {
src := LoadSSU2ChannelSizes()
if src == nil {
return nil
}
v, ok := src[ssu]
if !ok {
return nil
}
cp := make([]ChannelSize, len(v))
copy(cp, v)
return cp
}
func GetMeasurements(ctx context.Context, batchSize int) ([]*measurement, error) {
var totalRecords []*measurement
id := int64(0)
for {
var records []*measurement
result := client.WithContext(ctx).Table(tbmeasurement).Where("id > ?", id).
Order("id ASC").Limit(batchSize).Find(&records)
if result.Error != nil {
return totalRecords, result.Error
}
length := len(records)
if length <= 0 {
break
}
id = records[length-1].ID
totalRecords = append(totalRecords, records...)
}
return totalRecords, nil
}
func GenSSU2ChannelSizes(ctx context.Context, batchSize int) error {
id := int64(0)
ssu2ChannelSizes := make(map[string][]ChannelSize)
for {
var records []*measurement
result := client.WithContext(ctx).Table(tbmeasurement).
Where("id > ?", id).Order("id ASC").Limit(batchSize).Find(&records)
if result.Error != nil {
return result.Error
}
length := len(records)
if length <= 0 {
break
}
for _, record := range records {
if record == nil || record.DataSource == nil {
continue
}
addrType := record.DataSource.Type
addr := record.DataSource.Addr
if err := genMappingFromAddr(ssu2ChannelSizes, addrType, addr, record.Size); err != nil {
return err
}
}
id = records[length-1].ID
}
StoreSSU2ChannelSizes(ssu2ChannelSizes)
return nil
}
func genMappingFromAddr(ssu2ChannelSizes map[string][]ChannelSize, addrType int, addr any, size int) error {
switch addrType {
case 1:
if rawAddr, ok := addr.(map[string]interface{}); ok {
station, ok := rawAddr["station"].(string)
if !ok {
return errors.New("invalid station")
}
device, ok := rawAddr["device"].(string)
if !ok {
return errors.New("invalid device")
}
channel, ok := rawAddr["channel"].(string)
if !ok {
return errors.New("invalid channel")
}
ssu2ChannelSizes[device] = append(ssu2ChannelSizes[device], ChannelSize{
Station: station,
Device: device,
Channel: channel,
Size: size,
})
} else {
return errors.New("invalid io_address")
}
default:
return errors.New("invalid data_source.type")
}
return nil
}