| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- // Package faultRecordFile 故障录波数据解析(纯函数):cmd=0 的 hexData、cmd=2 导出的 failuredata/waveformdata 及压缩包。
- //
- // 字节结构依据 docs/fault-record/FaultFilter_MQTT协议文档.md 5.1.1、5.2,
- // 并按 docs/fault-record/protocol-specification.md 8A 的样本验证规则补充。
- package faultRecordFile
- import (
- "bytes"
- "encoding/binary"
- "fmt"
- proto "fwh-toolbox-server/function/FaultRecordProtocol"
- )
- // byteReader 顺序读取,越界时记录错误并返回零值
- type byteReader struct {
- b []byte
- p int
- err error
- }
- func (r *byteReader) take(n int, field string) []byte {
- if r.err != nil {
- return make([]byte, n)
- }
- if r.p+n > len(r.b) {
- r.err = fmt.Errorf("data too short at %s (offset %d, need %d, have %d)", field, r.p, n, len(r.b)-r.p)
- return make([]byte, n)
- }
- v := r.b[r.p : r.p+n]
- r.p += n
- return v
- }
- func (r *byteReader) u8(field string) uint8 { return r.take(1, field)[0] }
- func (r *byteReader) u16(field string) uint16 { return binary.BigEndian.Uint16(r.take(2, field)) }
- func (r *byteReader) u32(field string) uint32 { return binary.BigEndian.Uint32(r.take(4, field)) }
- func (r *byteReader) time(field string) proto.DeviceTime {
- return proto.ParseDeviceTime(r.take(7, field))
- }
- // cString 取第一个 NUL 之前的内容
- func cString(b []byte) string {
- if i := bytes.IndexByte(b, 0); i >= 0 {
- b = b[:i]
- }
- return string(b)
- }
- /************** 公共结构 **************/
- // BasicInfo 设备故障录波功能基本信息(code=2,16 字节)
- type BasicInfo struct {
- CanProtocolVersion uint8 `json:"canProtocolVersion"`
- StorageFormat uint8 `json:"storageFormat"`
- ParamListVersion string `json:"paramListVersion"` // 4 字节十六进制
- MaxSampleFrequency uint16 `json:"maxSampleFrequency"`
- SampleFrequency uint16 `json:"sampleFrequency"`
- TotalPoints uint16 `json:"totalPoints"`
- PrePoints uint16 `json:"prePoints"`
- PostPoints uint16 `json:"postPoints"`
- }
- func readBasicInfo(r *byteReader) BasicInfo {
- return BasicInfo{
- CanProtocolVersion: r.u8("canProtocolVersion"),
- StorageFormat: r.u8("storageFormat"),
- ParamListVersion: fmt.Sprintf("%08X", r.u32("paramListVersion")),
- MaxSampleFrequency: r.u16("maxSampleFrequency"),
- SampleFrequency: r.u16("sampleFrequency"),
- TotalPoints: r.u16("totalPoints"),
- PrePoints: r.u16("prePoints"),
- PostPoints: r.u16("postPoints"),
- }
- }
- // SnapshotChannel 快照通道:含义代码(F14),code=4 中带故障时刻的原始值
- type SnapshotChannel struct {
- Meaning uint16 `json:"meaning"`
- Value *uint16 `json:"value,omitempty"`
- }
- // ConfigurableChannel 可配通道:类型 2B + 数据 4B。
- // 类型低字节与设置 id=6 的类型字节格式一致:高 4 位为设置方式(0=默认方式 1=代码方式),低 4 位为地址类型;
- // 两种方式的数据都按 4 字节原样保留在 Address(代码方式时前 2 字节为参数代码)。
- type ConfigurableChannel struct {
- Type uint16 `json:"type"`
- Mode uint8 `json:"mode"`
- AddrType uint8 `json:"addrType"`
- TypeName string `json:"typeName"`
- Address uint32 `json:"address"`
- }
- // ChannelGroups 快照 / 采样 / 可配快照 / 可配采样 四组通道(code=1 与 failuredata 共用)
- type ChannelGroups struct {
- SnapshotTotal uint8 `json:"snapshotTotal"`
- SnapshotUsed uint8 `json:"snapshotUsed"`
- Snapshots []SnapshotChannel `json:"snapshots"`
- SampleTotal uint8 `json:"sampleTotal"`
- SampleUsed uint8 `json:"sampleUsed"`
- SampleMeanings []uint16 `json:"sampleMeanings"`
- ConfigSnapTotal uint8 `json:"configSnapTotal"`
- ConfigSnapUsed uint8 `json:"configSnapUsed"`
- ConfigSnapshots []ConfigurableChannel `json:"configSnapshots"`
- ConfigSampleTotal uint8 `json:"configSampleTotal"`
- ConfigSampleUsed uint8 `json:"configSampleUsed"`
- ConfigSamples []ConfigurableChannel `json:"configSamples"`
- }
- func readChannelGroups(r *byteReader, snapshotWithValue bool) ChannelGroups {
- var g ChannelGroups
- g.SnapshotTotal = r.u8("snapshotTotal")
- g.SnapshotUsed = r.u8("snapshotUsed")
- g.Snapshots = make([]SnapshotChannel, 0, g.SnapshotTotal)
- for i := 0; i < int(g.SnapshotTotal); i++ {
- c := SnapshotChannel{Meaning: r.u16("snapshotMeaning")}
- if snapshotWithValue {
- v := r.u16("snapshotValue")
- c.Value = &v
- }
- g.Snapshots = append(g.Snapshots, c)
- }
- g.SampleTotal = r.u8("sampleTotal")
- g.SampleUsed = r.u8("sampleUsed")
- g.SampleMeanings = make([]uint16, 0, g.SampleTotal)
- for i := 0; i < int(g.SampleTotal); i++ {
- g.SampleMeanings = append(g.SampleMeanings, r.u16("sampleMeaning"))
- }
- g.ConfigSnapTotal = r.u8("configSnapTotal")
- g.ConfigSnapUsed = r.u8("configSnapUsed")
- g.ConfigSnapshots = readConfigurable(r, int(g.ConfigSnapTotal))
- g.ConfigSampleTotal = r.u8("configSampleTotal")
- g.ConfigSampleUsed = r.u8("configSampleUsed")
- g.ConfigSamples = readConfigurable(r, int(g.ConfigSampleTotal))
- return g
- }
- func readConfigurable(r *byteReader, n int) []ConfigurableChannel {
- list := make([]ConfigurableChannel, 0, n)
- for i := 0; i < n; i++ {
- t := r.u16("configType")
- mode, addrType := uint8(t)>>4, uint8(t)&0x0F
- list = append(list, ConfigurableChannel{
- Type: t,
- Mode: mode,
- AddrType: addrType,
- TypeName: proto.ChannelAddrTypeNames[addrType],
- Address: r.u32("configAddress"),
- })
- }
- return list
- }
|