record.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package faultRecordFile
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. proto "fwh-toolbox-server/function/FaultRecordProtocol"
  6. )
  7. const versionFieldSize = 64
  8. // FailureData failuredata(code=4 单一故障信息)
  9. type FailureData struct {
  10. DeclaredLength uint16 `json:"declaredLength"` // 样本中等于整个文件字节数(含填充),不作为有效长度使用
  11. Basic BasicInfo `json:"basic"`
  12. Index uint8 `json:"index"`
  13. Time string `json:"time"` // 设备时间
  14. TriggerSource uint16 `json:"triggerSource"`
  15. TriggerSourceName string `json:"triggerSourceName,omitempty"`
  16. DcdcVersion string `json:"dcdcVersion"`
  17. InvVersion string `json:"invVersion,omitempty"` // aGate 中为填充,不解析
  18. FpgaVersion string `json:"fpgaVersion,omitempty"` // aGate 中为填充,不解析
  19. ChannelGroups
  20. ParsedBytes int `json:"parsedBytes"` // 结构结束位置,之后为填充
  21. }
  22. // ParseFailureData 按结构逐项解析,结构结束后的内容(0xFF 填充)忽略
  23. func ParseFailureData(b []byte, deviceType string) (FailureData, error) {
  24. r := &byteReader{b: b}
  25. f := FailureData{
  26. DeclaredLength: r.u16("length"),
  27. Basic: readBasicInfo(r),
  28. Index: r.u8("index"),
  29. Time: r.time("time").String(),
  30. TriggerSource: r.u16("triggerSource"),
  31. }
  32. f.TriggerSourceName = proto.TriggerSourceName(f.TriggerSource)
  33. f.DcdcVersion = cString(r.take(versionFieldSize, "dcdcVersion"))
  34. inv := r.take(versionFieldSize, "invVersion")
  35. fpga := r.take(versionFieldSize, "fpgaVersion")
  36. if deviceType != proto.DeviceTypeAGate {
  37. f.InvVersion = cString(inv)
  38. f.FpgaVersion = cString(fpga)
  39. }
  40. f.ChannelGroups = readChannelGroups(r, true)
  41. f.ParsedBytes = r.p
  42. return f, r.err
  43. }
  44. // WaveformChannel 一个采样通道的全部点(原始 int16 值)
  45. type WaveformChannel struct {
  46. Number int `json:"number"` // 通道序号,从 0 开始
  47. Meaning uint16 `json:"meaning"` // 采样通道含义(可配通道处为类型码)
  48. // Configurable 采样通道列表末尾的通道对应可配采样通道(按样本推断)
  49. Configurable *ConfigurableChannel `json:"configurable,omitempty"`
  50. Values []int16 `json:"values"`
  51. }
  52. // Waveform waveformdata(code=5 单一故障波形数据)
  53. type Waveform struct {
  54. Points int `json:"points"`
  55. PrePoints int `json:"prePoints"`
  56. SampleFrequency int `json:"sampleFrequency"`
  57. Channels []WaveformChannel `json:"channels"`
  58. }
  59. // ParseWaveform 采样点优先排列:先排第 1 点的 n 个通道,再排第 2 点,依此类推。
  60. // 通道数取 failuredata 的“采样通道总数”,点数取基本信息的“采样点总数”;每点每通道 2 字节、大端、按 int16 解释。
  61. func ParseWaveform(b []byte, fd FailureData) (Waveform, error) {
  62. n := int(fd.SampleTotal)
  63. m := int(fd.Basic.TotalPoints)
  64. if n == 0 || m == 0 {
  65. return Waveform{}, fmt.Errorf("invalid waveform shape: %d channels × %d points", n, m)
  66. }
  67. if len(b) != n*m*2 {
  68. return Waveform{}, fmt.Errorf("waveform length %d does not match %d channels × %d points × 2", len(b), n, m)
  69. }
  70. w := Waveform{
  71. Points: m,
  72. PrePoints: int(fd.Basic.PrePoints),
  73. SampleFrequency: int(fd.Basic.SampleFrequency),
  74. Channels: make([]WaveformChannel, n),
  75. }
  76. cfgStart := n - int(fd.ConfigSampleTotal)
  77. for c := 0; c < n; c++ {
  78. ch := WaveformChannel{Number: c, Values: make([]int16, m)}
  79. if c < len(fd.SampleMeanings) {
  80. ch.Meaning = fd.SampleMeanings[c]
  81. }
  82. if cfgStart >= 0 && c >= cfgStart && c-cfgStart < len(fd.ConfigSamples) {
  83. cfg := fd.ConfigSamples[c-cfgStart]
  84. ch.Configurable = &cfg
  85. }
  86. w.Channels[c] = ch
  87. }
  88. for k := 0; k < m; k++ {
  89. for c := 0; c < n; c++ {
  90. off := (k*n + c) * 2
  91. w.Channels[c].Values[k] = int16(binary.BigEndian.Uint16(b[off:]))
  92. }
  93. }
  94. return w, nil
  95. }
  96. // TimeOffsetMs 第 k 个采样点相对故障时刻的毫秒数
  97. func (w Waveform) TimeOffsetMs(k int) float64 {
  98. if w.SampleFrequency == 0 {
  99. return 0
  100. }
  101. return float64(k-w.PrePoints) * 1000 / float64(w.SampleFrequency)
  102. }
  103. // Record 一条已解析的录波记录
  104. type Record struct {
  105. Batch string `json:"batch"`
  106. Sn string `json:"sn"`
  107. RecordIndex int `json:"recordIndex"`
  108. Failure FailureData `json:"failure"`
  109. Waveform Waveform `json:"waveform"`
  110. }
  111. // ParseRecordFiles 解析一条记录的两个文件(内容为十六进制文本)
  112. func ParseRecordFiles(f RecordFiles, deviceType string) (Record, error) {
  113. rec := Record{Batch: f.Batch, Sn: f.Sn, RecordIndex: f.Index}
  114. fdBytes, err := proto.DecodeHex(string(f.FailureData))
  115. if err != nil {
  116. return rec, fmt.Errorf("failuredata: %w", err)
  117. }
  118. if rec.Failure, err = ParseFailureData(fdBytes, deviceType); err != nil {
  119. return rec, fmt.Errorf("failuredata: %w", err)
  120. }
  121. wfBytes, err := proto.DecodeHex(string(f.WaveformData))
  122. if err != nil {
  123. return rec, fmt.Errorf("waveformdata: %w", err)
  124. }
  125. if rec.Waveform, err = ParseWaveform(wfBytes, rec.Failure); err != nil {
  126. return rec, fmt.Errorf("waveformdata: %w", err)
  127. }
  128. return rec, nil
  129. }