package postgres import ( "context" "database/sql/driver" "encoding/json" "errors" "sync" "sync/atomic" ) const ( tbmeasurement string = "public.measurement" ) const ( ChannelYCPrefix string = "TM" ChannelYXPrefix string = "TS" ChannelP string = "P" ChannelQ string = "Q" ChannelS string = "S" ChannelPF string = "PF" ChannelF string = "F" ChannelDF string = "dF" 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"` // others } type ChannelSize struct { Station string Device string Channel string Size int } // channel is original var SSU2ChannelSizes atomic.Value var ChannelSizes sync.Map 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) ssu2Channel2Exist := make(map[string]map[string]struct{}) 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 } if err := genMappingFromAddr(ssu2ChannelSizes, ssu2Channel2Exist, record); err != nil { return err } } id = records[length-1].ID } StoreSSU2ChannelSizes(ssu2ChannelSizes) return nil } func genMappingFromAddr(ssu2ChannelSizes map[string][]ChannelSize, ssu2Channel2Exist map[string]map[string]struct{}, record *measurement) error { switch record.DataSource.Type { case 1: if rawAddr, ok := record.DataSource.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") } if _, ok := ssu2Channel2Exist[device]; !ok { ssu2Channel2Exist[device] = make(map[string]struct{}) } if _, ok := ssu2Channel2Exist[device][channel]; ok { return nil } else { ssu2Channel2Exist[device][channel] = struct{}{} } channelSize := ChannelSize{ Station: station, Device: device, Channel: channel, Size: record.Size, } ChannelSizes.Store(record.Tag, channelSize) ssu2ChannelSizes[device] = append(ssu2ChannelSizes[device], channelSize) } else { return errors.New("invalid io_address") } case 2: default: return errors.New("invalid data_source.type") } return nil }