package faultRecordFile import ( "encoding/binary" "fmt" proto "fwh-toolbox-server/function/FaultRecordProtocol" ) const versionFieldSize = 64 // FailureData failuredata(code=4 单一故障信息) type FailureData struct { DeclaredLength uint16 `json:"declaredLength"` // 样本中等于整个文件字节数(含填充),不作为有效长度使用 Basic BasicInfo `json:"basic"` Index uint8 `json:"index"` Time string `json:"time"` // 设备时间 TriggerSource uint16 `json:"triggerSource"` TriggerSourceName string `json:"triggerSourceName,omitempty"` DcdcVersion string `json:"dcdcVersion"` InvVersion string `json:"invVersion,omitempty"` // aGate 中为填充,不解析 FpgaVersion string `json:"fpgaVersion,omitempty"` // aGate 中为填充,不解析 ChannelGroups ParsedBytes int `json:"parsedBytes"` // 结构结束位置,之后为填充 } // ParseFailureData 按结构逐项解析,结构结束后的内容(0xFF 填充)忽略 func ParseFailureData(b []byte, deviceType string) (FailureData, error) { r := &byteReader{b: b} f := FailureData{ DeclaredLength: r.u16("length"), Basic: readBasicInfo(r), Index: r.u8("index"), Time: r.time("time").String(), TriggerSource: r.u16("triggerSource"), } f.TriggerSourceName = proto.TriggerSourceName(f.TriggerSource) f.DcdcVersion = cString(r.take(versionFieldSize, "dcdcVersion")) inv := r.take(versionFieldSize, "invVersion") fpga := r.take(versionFieldSize, "fpgaVersion") if deviceType != proto.DeviceTypeAGate { f.InvVersion = cString(inv) f.FpgaVersion = cString(fpga) } f.ChannelGroups = readChannelGroups(r, true) f.ParsedBytes = r.p return f, r.err } // WaveformChannel 一个采样通道的全部点(原始 int16 值) type WaveformChannel struct { Number int `json:"number"` // 通道序号,从 0 开始 Meaning uint16 `json:"meaning"` // 采样通道含义(可配通道处为类型码) // Configurable 采样通道列表末尾的通道对应可配采样通道(按样本推断) Configurable *ConfigurableChannel `json:"configurable,omitempty"` Values []int16 `json:"values"` } // Waveform waveformdata(code=5 单一故障波形数据) type Waveform struct { Points int `json:"points"` PrePoints int `json:"prePoints"` SampleFrequency int `json:"sampleFrequency"` Channels []WaveformChannel `json:"channels"` } // ParseWaveform 采样点优先排列:先排第 1 点的 n 个通道,再排第 2 点,依此类推。 // 通道数取 failuredata 的“采样通道总数”,点数取基本信息的“采样点总数”;每点每通道 2 字节、大端、按 int16 解释。 func ParseWaveform(b []byte, fd FailureData) (Waveform, error) { n := int(fd.SampleTotal) m := int(fd.Basic.TotalPoints) if n == 0 || m == 0 { return Waveform{}, fmt.Errorf("invalid waveform shape: %d channels × %d points", n, m) } if len(b) != n*m*2 { return Waveform{}, fmt.Errorf("waveform length %d does not match %d channels × %d points × 2", len(b), n, m) } w := Waveform{ Points: m, PrePoints: int(fd.Basic.PrePoints), SampleFrequency: int(fd.Basic.SampleFrequency), Channels: make([]WaveformChannel, n), } cfgStart := n - int(fd.ConfigSampleTotal) for c := 0; c < n; c++ { ch := WaveformChannel{Number: c, Values: make([]int16, m)} if c < len(fd.SampleMeanings) { ch.Meaning = fd.SampleMeanings[c] } if cfgStart >= 0 && c >= cfgStart && c-cfgStart < len(fd.ConfigSamples) { cfg := fd.ConfigSamples[c-cfgStart] ch.Configurable = &cfg } w.Channels[c] = ch } for k := 0; k < m; k++ { for c := 0; c < n; c++ { off := (k*n + c) * 2 w.Channels[c].Values[k] = int16(binary.BigEndian.Uint16(b[off:])) } } return w, nil } // TimeOffsetMs 第 k 个采样点相对故障时刻的毫秒数 func (w Waveform) TimeOffsetMs(k int) float64 { if w.SampleFrequency == 0 { return 0 } return float64(k-w.PrePoints) * 1000 / float64(w.SampleFrequency) } // Record 一条已解析的录波记录 type Record struct { Batch string `json:"batch"` Sn string `json:"sn"` RecordIndex int `json:"recordIndex"` Failure FailureData `json:"failure"` Waveform Waveform `json:"waveform"` } // ParseRecordFiles 解析一条记录的两个文件(内容为十六进制文本) func ParseRecordFiles(f RecordFiles, deviceType string) (Record, error) { rec := Record{Batch: f.Batch, Sn: f.Sn, RecordIndex: f.Index} fdBytes, err := proto.DecodeHex(string(f.FailureData)) if err != nil { return rec, fmt.Errorf("failuredata: %w", err) } if rec.Failure, err = ParseFailureData(fdBytes, deviceType); err != nil { return rec, fmt.Errorf("failuredata: %w", err) } wfBytes, err := proto.DecodeHex(string(f.WaveformData)) if err != nil { return rec, fmt.Errorf("waveformdata: %w", err) } if rec.Waveform, err = ParseWaveform(wfBytes, rec.Failure); err != nil { return rec, fmt.Errorf("waveformdata: %w", err) } return rec, nil }