reader.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // Package faultRecordFile 故障录波数据解析(纯函数):cmd=0 的 hexData、cmd=2 导出的 failuredata/waveformdata 及压缩包。
  2. //
  3. // 字节结构依据 docs/fault-record/FaultFilter_MQTT协议文档.md 5.1.1、5.2,
  4. // 并按 docs/fault-record/protocol-specification.md 8A 的样本验证规则补充。
  5. package faultRecordFile
  6. import (
  7. "bytes"
  8. "encoding/binary"
  9. "fmt"
  10. proto "fwh-toolbox-server/function/FaultRecordProtocol"
  11. )
  12. // byteReader 顺序读取,越界时记录错误并返回零值
  13. type byteReader struct {
  14. b []byte
  15. p int
  16. err error
  17. }
  18. func (r *byteReader) take(n int, field string) []byte {
  19. if r.err != nil {
  20. return make([]byte, n)
  21. }
  22. if r.p+n > len(r.b) {
  23. r.err = fmt.Errorf("data too short at %s (offset %d, need %d, have %d)", field, r.p, n, len(r.b)-r.p)
  24. return make([]byte, n)
  25. }
  26. v := r.b[r.p : r.p+n]
  27. r.p += n
  28. return v
  29. }
  30. func (r *byteReader) u8(field string) uint8 { return r.take(1, field)[0] }
  31. func (r *byteReader) u16(field string) uint16 { return binary.BigEndian.Uint16(r.take(2, field)) }
  32. func (r *byteReader) u32(field string) uint32 { return binary.BigEndian.Uint32(r.take(4, field)) }
  33. func (r *byteReader) time(field string) proto.DeviceTime {
  34. return proto.ParseDeviceTime(r.take(7, field))
  35. }
  36. // cString 取第一个 NUL 之前的内容
  37. func cString(b []byte) string {
  38. if i := bytes.IndexByte(b, 0); i >= 0 {
  39. b = b[:i]
  40. }
  41. return string(b)
  42. }
  43. /************** 公共结构 **************/
  44. // BasicInfo 设备故障录波功能基本信息(code=2,16 字节)
  45. type BasicInfo struct {
  46. CanProtocolVersion uint8 `json:"canProtocolVersion"`
  47. StorageFormat uint8 `json:"storageFormat"`
  48. ParamListVersion string `json:"paramListVersion"` // 4 字节十六进制
  49. MaxSampleFrequency uint16 `json:"maxSampleFrequency"`
  50. SampleFrequency uint16 `json:"sampleFrequency"`
  51. TotalPoints uint16 `json:"totalPoints"`
  52. PrePoints uint16 `json:"prePoints"`
  53. PostPoints uint16 `json:"postPoints"`
  54. }
  55. func readBasicInfo(r *byteReader) BasicInfo {
  56. return BasicInfo{
  57. CanProtocolVersion: r.u8("canProtocolVersion"),
  58. StorageFormat: r.u8("storageFormat"),
  59. ParamListVersion: fmt.Sprintf("%08X", r.u32("paramListVersion")),
  60. MaxSampleFrequency: r.u16("maxSampleFrequency"),
  61. SampleFrequency: r.u16("sampleFrequency"),
  62. TotalPoints: r.u16("totalPoints"),
  63. PrePoints: r.u16("prePoints"),
  64. PostPoints: r.u16("postPoints"),
  65. }
  66. }
  67. // SnapshotChannel 快照通道:含义代码(F14),code=4 中带故障时刻的原始值
  68. type SnapshotChannel struct {
  69. Meaning uint16 `json:"meaning"`
  70. Value *uint16 `json:"value,omitempty"`
  71. }
  72. // ConfigurableChannel 可配通道:类型 2B + 数据 4B。
  73. // 类型低字节与设置 id=6 的类型字节格式一致:高 4 位为设置方式(0=默认方式 1=代码方式),低 4 位为地址类型;
  74. // 两种方式的数据都按 4 字节原样保留在 Address(代码方式时前 2 字节为参数代码)。
  75. type ConfigurableChannel struct {
  76. Type uint16 `json:"type"`
  77. Mode uint8 `json:"mode"`
  78. AddrType uint8 `json:"addrType"`
  79. TypeName string `json:"typeName"`
  80. Address uint32 `json:"address"`
  81. }
  82. // ChannelGroups 快照 / 采样 / 可配快照 / 可配采样 四组通道(code=1 与 failuredata 共用)
  83. type ChannelGroups struct {
  84. SnapshotTotal uint8 `json:"snapshotTotal"`
  85. SnapshotUsed uint8 `json:"snapshotUsed"`
  86. Snapshots []SnapshotChannel `json:"snapshots"`
  87. SampleTotal uint8 `json:"sampleTotal"`
  88. SampleUsed uint8 `json:"sampleUsed"`
  89. SampleMeanings []uint16 `json:"sampleMeanings"`
  90. ConfigSnapTotal uint8 `json:"configSnapTotal"`
  91. ConfigSnapUsed uint8 `json:"configSnapUsed"`
  92. ConfigSnapshots []ConfigurableChannel `json:"configSnapshots"`
  93. ConfigSampleTotal uint8 `json:"configSampleTotal"`
  94. ConfigSampleUsed uint8 `json:"configSampleUsed"`
  95. ConfigSamples []ConfigurableChannel `json:"configSamples"`
  96. }
  97. func readChannelGroups(r *byteReader, snapshotWithValue bool) ChannelGroups {
  98. var g ChannelGroups
  99. g.SnapshotTotal = r.u8("snapshotTotal")
  100. g.SnapshotUsed = r.u8("snapshotUsed")
  101. g.Snapshots = make([]SnapshotChannel, 0, g.SnapshotTotal)
  102. for i := 0; i < int(g.SnapshotTotal); i++ {
  103. c := SnapshotChannel{Meaning: r.u16("snapshotMeaning")}
  104. if snapshotWithValue {
  105. v := r.u16("snapshotValue")
  106. c.Value = &v
  107. }
  108. g.Snapshots = append(g.Snapshots, c)
  109. }
  110. g.SampleTotal = r.u8("sampleTotal")
  111. g.SampleUsed = r.u8("sampleUsed")
  112. g.SampleMeanings = make([]uint16, 0, g.SampleTotal)
  113. for i := 0; i < int(g.SampleTotal); i++ {
  114. g.SampleMeanings = append(g.SampleMeanings, r.u16("sampleMeaning"))
  115. }
  116. g.ConfigSnapTotal = r.u8("configSnapTotal")
  117. g.ConfigSnapUsed = r.u8("configSnapUsed")
  118. g.ConfigSnapshots = readConfigurable(r, int(g.ConfigSnapTotal))
  119. g.ConfigSampleTotal = r.u8("configSampleTotal")
  120. g.ConfigSampleUsed = r.u8("configSampleUsed")
  121. g.ConfigSamples = readConfigurable(r, int(g.ConfigSampleTotal))
  122. return g
  123. }
  124. func readConfigurable(r *byteReader, n int) []ConfigurableChannel {
  125. list := make([]ConfigurableChannel, 0, n)
  126. for i := 0; i < n; i++ {
  127. t := r.u16("configType")
  128. mode, addrType := uint8(t)>>4, uint8(t)&0x0F
  129. list = append(list, ConfigurableChannel{
  130. Type: t,
  131. Mode: mode,
  132. AddrType: addrType,
  133. TypeName: proto.ChannelAddrTypeNames[addrType],
  134. Address: r.u32("configAddress"),
  135. })
  136. }
  137. return list
  138. }