modelRT/model/measurement_model.go

272 lines
7.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package model define model struct of model runtime service
package model
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"modelRT/constants"
)
// MeasurementDataSource define measurement data source struct
type MeasurementDataSource struct {
Type int `json:"type"`
IOAddress IOAddress `json:"io_address"`
}
// IOAddress define interface of IO address
type IOAddress any
// CL3611Address define CL3611 protol struct
type CL3611Address struct {
Station string `json:"station"`
Device string `json:"device"`
Channel string `json:"channel"`
}
// Power104Address define electricity 104 protol struct
type Power104Address struct {
Station string `json:"station"`
Packet int `json:"packet"`
Offset int `json:"offset"`
}
// NewCL3611DataSource define func of create CL3611 data source
func NewCL3611DataSource(station, device, channel string) (*MeasurementDataSource, error) {
return &MeasurementDataSource{
Type: constants.DataSourceTypeCL3611,
IOAddress: CL3611Address{
Station: station,
Device: device,
Channel: channel,
},
}, nil
}
// NewPower104DataSource define func of create Power104 data source
func NewPower104DataSource(station string, packet, offset int) (*MeasurementDataSource, error) {
return &MeasurementDataSource{
Type: constants.DataSourceTypePower104,
IOAddress: Power104Address{
Station: station,
Packet: packet,
Offset: offset,
},
}, nil
}
func generateChannelName(prefix string, number int, suffix string) (string, error) {
switch prefix {
case constants.ChannelPrefixTelemetry:
if number > 10 {
return "", constants.ErrExceedsLimitType
}
var builder strings.Builder
numberStr := strconv.Itoa(number)
builder.Grow(len(prefix) + len(numberStr) + len(suffix))
builder.WriteString(prefix)
builder.WriteString(numberStr)
builder.WriteString(suffix)
channelName := builder.String()
return channelName, nil
case constants.ChannelPrefixTelesignal:
var numberStr string
if number < 10 {
numberStr = "0" + strconv.Itoa(number)
}
numberStr = strconv.Itoa(number)
var builder strings.Builder
builder.Grow(len(prefix) + len(numberStr) + len(suffix))
builder.WriteString(prefix)
builder.WriteString(numberStr)
builder.WriteString(suffix)
channelName := builder.String()
return channelName, nil
default:
return "", constants.ErrUnsupportedChannelPrefixType
}
}
// NewTelemetryChannel define func of generate telemetry channel CL3611 data source
func NewTelemetryChannel(station, device, channelNameSuffix string, channelNumber int) (*MeasurementDataSource, error) {
channelName, err := generateChannelName(constants.ChannelPrefixTelemetry, channelNumber, channelNameSuffix)
if err != nil {
return nil, fmt.Errorf("failed to generate channel name: %w", err)
}
return NewCL3611DataSource(station, device, channelName)
}
// NewTelesignalChannel define func of generate telesignal channel CL3611 data source
func NewTelesignalChannel(station, device, channelNameSuffix string, channelNumber int) (*MeasurementDataSource, error) {
channelName, err := generateChannelName(constants.ChannelPrefixTelesignal, channelNumber, channelNameSuffix)
if err != nil {
return nil, fmt.Errorf("failed to generate channel name: %w", err)
}
return NewCL3611DataSource(station, device, channelName)
}
// NewStandardChannel define func of generate standard channel CL3611 data source
func NewStandardChannel(station, device, channelType string) (*MeasurementDataSource, error) {
return NewCL3611DataSource(station, device, channelType)
}
// ParseDataSourceFromJSON define func of parse data source from json string
func ParseDataSourceFromJSON(jsonStr string) (MeasurementDataSource, error) {
var data struct {
Type int `json:"type"`
IOAddress json.RawMessage `json:"io_address"`
}
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
return MeasurementDataSource{}, err
}
var source MeasurementDataSource
source.Type = data.Type
switch data.Type {
case constants.DataSourceTypeCL3611:
var addr CL3611Address
if err := json.Unmarshal(data.IOAddress, &addr); err != nil {
return MeasurementDataSource{}, err
}
source.IOAddress = addr
case constants.DataSourceTypePower104:
var addr Power104Address
if err := json.Unmarshal(data.IOAddress, &addr); err != nil {
return MeasurementDataSource{}, err
}
source.IOAddress = addr
default:
// 对于未知类型保持原始JSON数据
source.IOAddress = data.IOAddress
}
return source, nil
}
// ToJSON define func of convert data source to json string
func (m MeasurementDataSource) ToJSON() (string, error) {
bytes, err := json.Marshal(m)
if err != nil {
return "", err
}
return string(bytes), nil
}
// GetIOAddress define func of get IO address with correct type
func (m MeasurementDataSource) GetIOAddress() (IOAddress, error) {
switch m.Type {
case constants.DataSourceTypeCL3611:
if addr, ok := m.IOAddress.(CL3611Address); ok {
return addr, nil
}
return nil, constants.ErrInvalidAddressType
case constants.DataSourceTypePower104:
if addr, ok := m.IOAddress.(Power104Address); ok {
return addr, nil
}
return nil, constants.ErrInvalidAddressType
default:
return nil, constants.ErrUnknownDataType
}
}
// GenerateMeasureIdentifier define func of generate measurement identifier
func GenerateMeasureIdentifier(source map[string]any) (string, error) {
regTypeVal, ok := source["type"]
if !ok {
return "", fmt.Errorf("can not find type in datasource field")
}
var regType int
switch v := regTypeVal.(type) {
case int:
regType = v
case float32:
if v != float32(int(v)) {
return "", fmt.Errorf("invalid type format in datasource field, expected integer value, got float: %f", v)
}
regType = int(v)
case float64:
if v != float64(int(v)) {
return "", fmt.Errorf("invalid type format in datasource field, expected integer value, got float: %f", v)
}
regType = int(v)
default:
return "", fmt.Errorf("invalid type format in datasource field,%T", v)
}
ioAddrVal, ok := source["io_address"]
if !ok {
return "", fmt.Errorf("can not find io_address from datasource field")
}
ioAddress, ok := ioAddrVal.(map[string]any)
if !ok {
return "", fmt.Errorf("io_address field is not a valid map")
}
switch regType {
case constants.DataSourceTypeCL3611:
station, ok := ioAddress["station"].(string)
if !ok {
return "", fmt.Errorf("CL3611:invalid or missing station field")
}
device, ok := ioAddress["device"].(string)
if !ok {
return "", fmt.Errorf("CL3611:invalid or missing device field")
}
// 提取 channel (string)
channel, ok := ioAddress["channel"].(string)
if !ok {
return "", fmt.Errorf("CL3611:invalid or missing channel field")
}
return concatCL361WithPlus(station, device, channel), nil
case constants.DataSourceTypePower104:
station, ok := ioAddress["station"].(string)
if !ok {
return "", fmt.Errorf("Power104:invalid or missing station field")
}
packetVal, ok := ioAddress["packet"]
if !ok {
return "", fmt.Errorf("Power104: missing packet field")
}
var packet int
switch v := packetVal.(type) {
case int:
packet = v
default:
return "", fmt.Errorf("Power104:invalid packet format")
}
offsetVal, ok := ioAddress["offset"]
if !ok {
return "", fmt.Errorf("Power104:missing offset field")
}
var offset int
switch v := offsetVal.(type) {
case int:
offset = v
default:
return "", fmt.Errorf("Power104:invalid offset format")
}
return concatP104WithPlus(station, packet, offset), nil
default:
return "", fmt.Errorf("unsupport regulation type %d into datasource field", regType)
}
}
func concatP104WithPlus(station string, packet int, offset int) string {
packetStr := strconv.Itoa(packet)
offsetStr := strconv.Itoa(offset)
return station + ":" + packetStr + ":" + offsetStr
}
func concatCL361WithPlus(station, device, channel string) string {
return station + ":" + device + ":" + "phasor" + ":" + channel
}