-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathasdu.go
152 lines (144 loc) · 4.92 KB
/
asdu.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package iec104
import (
"encoding/binary"
"fmt"
"log"
"math"
"time"
)
//ASDU 应用服务数据单元
type ASDU struct {
TypeID byte //类型标识
Sequence bool //是否连续
Length byte //可变结构限定词
Cause uint16 //传输原因
PublicAddress uint16 //公共地址
Ts float64 //毫秒级时间戳
}
//数据类型
const (
//MSpNa1 不带游标的单点遥信,3个字节的地址,1个字节的值
MSpNa1 = 1
//MDpNa1 不带时标的双点遥信,每个遥信占1个字节
MDpNa1 = 3
//MMeNc1 带品质描述的测量值,每个遥测值占3个字节
MMeNa1 = 9
//MMeNc1 带品质描述的浮点值,每个遥测值占5个字节
MMeNc1 = 13
//MItNa1 电度总量,每个遥脉值占5个字节
MItNa1 = 15
//MSpTb1 带游标的单点遥信,3个字节的地址,1个字节的值,7个字节短时标
MSpTb1 = 30
//MEiNA1 初始化结束
MEiNA1 = 70
//CIcNa1 总召唤
CIcNa1 = 100
//CCiNa1 电度总召唤
CCiNa1 = 101
)
// ParseASDU 解析asdu
func (asdu *ASDU) ParseASDU(asduBytes []byte) (signals []*Signal, err error) {
signals = make([]*Signal, 0, 0)
if asduBytes == nil || len(asduBytes) < 4 {
err = fmt.Errorf("asdu[%X]非法", asduBytes)
return
}
asdu.TypeID = asduBytes[0]
//数据是否连续
asdu.Sequence, asdu.Length = asdu.ParseVariable(asduBytes[1])
var firstAddress uint32
asdu.Cause = binary.LittleEndian.Uint16([]byte{asduBytes[2], asduBytes[3]})
asdu.PublicAddress = binary.LittleEndian.Uint16([]byte{asduBytes[4], asduBytes[5]})
if asdu.Sequence {
firstAddress = binary.LittleEndian.Uint32([]byte{asduBytes[6], asduBytes[7], asduBytes[8], 0x00})
}
for i := 0; i < int(asdu.Length); i++ {
s := new(Signal)
s.TypeID = uint(asdu.TypeID)
if asdu.Sequence {
s.Address = firstAddress
firstAddress++
}
switch asdu.TypeID {
case MSpNa1, MDpNa1:
size := 4
if asdu.Sequence {
s.Value = float64(asduBytes[9+i])
} else {
s.Address = binary.LittleEndian.Uint32([]byte{asduBytes[6+i*size], asduBytes[6+i*size+1], asduBytes[6+i*size+2], 0x00})
s.Value = float64(asduBytes[6+i*size+3])
}
case MMeNa1:
size := 6
if asdu.Sequence {
size := 3
s.Value = float64(binary.LittleEndian.Uint16([]byte{asduBytes[9+i*size], asduBytes[9+i*size+1]}))
s.Quality = asduBytes[9+i*size+2]
} else {
s.Address = binary.LittleEndian.Uint32([]byte{asduBytes[6+i*size], asduBytes[6+i*size+1], asduBytes[6+i*size+2], 0x00})
s.Value = float64(binary.LittleEndian.Uint16([]byte{asduBytes[6+i*size+3], asduBytes[6+i*size+4]}))
s.Quality = asduBytes[6+i*size+5]
}
case MMeNc1:
size := 8
if asdu.Sequence {
size := 5
s.Value = float64(math.Float32frombits(binary.LittleEndian.Uint32([]byte{asduBytes[9+i*size], asduBytes[9+i*size+1],
asduBytes[9+i*size+2], asduBytes[9+i*size+3]})))
s.Quality = asduBytes[9+i*size+4]
} else {
s.Address = binary.LittleEndian.Uint32([]byte{asduBytes[6+i*size], asduBytes[6+i*size+1], asduBytes[6+i*size+2], 0x00})
s.Value = float64(math.Float32frombits(binary.LittleEndian.Uint32([]byte{asduBytes[6+i*size+3], asduBytes[6+i*size+4],
asduBytes[6+i*size+5], asduBytes[6+i*size+6]})))
s.Quality = asduBytes[6+i*size+7]
}
case MItNa1:
size := 8
if asdu.Sequence {
size := 5
s.Value = float64(binary.LittleEndian.Uint32([]byte{asduBytes[9+i*size], asduBytes[9+i*size+1],
asduBytes[9+i*size+2], asduBytes[9+i*size+3]}))
} else {
s.Address = binary.LittleEndian.Uint32([]byte{asduBytes[6+i*size], asduBytes[6+i*size+1], asduBytes[6+i*size+2], 0x00})
s.Value = float64(binary.LittleEndian.Uint32([]byte{asduBytes[6+i*size+3], asduBytes[9+i*size+4],
asduBytes[9+i*size+5], asduBytes[9+i*size+6]}))
}
case MSpTb1:
size := 11
s.Address = binary.LittleEndian.Uint32([]byte{asduBytes[6+i*size], asduBytes[6+i*size+1], asduBytes[6+i*size+2], 0x00})
s.Value = float64(asduBytes[6+i*size+3])
s.Ts = asdu.ParseTime(asduBytes[6+i*size+4 : 6+i*size+11])
case CIcNa1, CCiNa1, MEiNA1:
default:
log.Fatalln("暂不支持的数据类型:", asdu.TypeID)
}
signals = append(signals, s)
}
return
}
// ParseVariable 解析asdu可变结构限定词
func (asdu *ASDU) ParseVariable(b byte) (sq bool, length byte) {
//最高位是否为1
sq = b&128>>7 == 1
if sq {
length = b - 1<<7
return
}
length = b
return
}
// ParseTime 解析asdu中7个字节时表,转为带毫秒的时间戳
func (asdu *ASDU) ParseTime(asduBytes []byte) float64 {
if len(asduBytes) != 7 {
return 0
}
milliseconds := binary.LittleEndian.Uint16([]byte{asduBytes[0], asduBytes[1]})
nanosecond := (int(milliseconds) % 1000) * 1000000
second := int(milliseconds / 1000)
minute := int(asduBytes[2])
hour := int(asduBytes[3])
day := int(asduBytes[4])
month := int(asduBytes[5])
year := int(asduBytes[6]) + 2000
return float64(time.Date(year, time.Month(month), day, hour, minute, second, nanosecond, time.Local).Unix()) + float64(nanosecond)/1000000000.0
}