重构MySMS

三年前, 我搞了一个短信猫玩, 所以就写了个控制短信猫的程序, 详见这里

说是重构, 几乎就是除了界面部分都重写了一遍。

原来的设计, 使用了类似于同步的一种方式:

向串口发送命令, 然后读取并处理返回的信息, 就这么简单。

但是, 这个方式显然不能处理突发情况, 也就是短信猫主动发送数据的情况, 比如收到了新短信或者是有电话打进来。而是必须要等到下一次发送命令的时候, 才有可能处理得到。

“有可能”是因为, 我处理返回信息实际上就是判断里面有没有”OK”或者”ERROR”字样, 显然不会处理别的东西。

于是就这么用了几年。

现在的设计, 多开了个线程来读取串口发来的所有数据:

串口接收到的数据以行为单位, 一行就是一条数据。

使用了ManualResetEvent。当串口发来OK或者ERROR的时候, 保存这个OK或者ERROR的消息并设置这个event为非阻塞状态, 允许发送线程继续; 当串口发来其他数据, 比如收到了新短信或者是有电话打进来的时候, 就调用相应的处理程序。

串口不能同时读写, 并且读取操作是阻塞的, 于是在读取的时候就不能写入了。为了解决这个问题, 我设置了读取的超时时间, 并使用了同步锁。

同时, 顺便解决了接收短信有时候用TEXT模式不正常的问题, 使用了PDU格式。

下面是解析PDU的代码。

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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
Imports System.Text
 
Public Class PDUHelper
 
    Public Shared Function ToPDU(sms As ShortMessage) As String
        'TODO: To PDU
        Throw New NotImplementedException()
    End Function
 
    Public Shared Function Parse(raw As String) As ShortMessage
        Dim offset = 0
        Dim SMSCLength = HexToByte(raw.Substring(offset, 2))
        offset += 2
        Dim SMSC = DecodeAddress(raw.Substring(offset, SMSCLength * 2))
        offset += SMSCLength * 2
        Dim Unknown = HexToByte(raw.Substring(offset, 2))
        offset += 2
        Dim RemoteAddressLength = HexToByte(raw.Substring(offset, 2))
        offset += 2
        '转换成字节数
        If RemoteAddressLength Mod 2 = 1 Then
            RemoteAddressLength += 1
        End If
        RemoteAddressLength = RemoteAddressLength / 2 + 1
        Dim RemoteAddress = DecodeAddress(raw.Substring(offset, RemoteAddressLength * 2))
        offset += RemoteAddressLength * 2
        Dim PID = HexToByte(raw.Substring(offset, 2))
        offset += 2
        Dim DCS = HexToByte(raw.Substring(offset, 2))
        offset += 2
        Dim SendTime = DecodeTime(raw.Substring(offset, 14))
        offset += 14
        Dim UDL = HexToByte(raw.Substring(offset, 2))
        offset += 2
 
        If DCS = 0 Then '7bit时, UDL存的是真实字符的个数, 要转换为字节数
            UDL = Math.Ceiling(UDL * 7 / 8 )
        End If
 
        Dim UD = HexToBytes(raw.Substring(offset, UDL * 2))
        offset += UDL * 2
        Dim UD_string = ""
        If DCS = 0 Then '7bit
            UD_string = Decode7bit(Math.Floor(UDL * 8 / 7), UD)
        ElseIf DCS = 8 Then 'UCS2
            UD_string = DecodeUCS2(UD)
        Else
            UD_string = BytesToHex(UD)
            Utils.dbg("Warning: Unknown DCS, returning raw data")
        End If
 
        Dim sms As New ShortMessage
        sms.Message = UD_string
        sms.MessageCentre = SMSC
        sms.Time = SendTime
        sms.RemoteAddress = RemoteAddress
        Return sms
    End Function
 
    Public Shared Function DecodeTime(raw As String) As DateTime
        Dim year = Now.Year.ToString().Substring(0, 2) + raw(1) + raw(0)
        Dim month = raw(3) + raw(2)
        Dim day = raw(5) + raw(4)
        Dim hour = raw(7) + raw(6)
        Dim minute = raw(9) + raw(8)
        Dim second = raw(11) + raw(10)
        Dim offset = HexToByte(raw(13) + raw(12))
        Return New DateTime(year, month, day, hour, minute, second)
    End Function
 
    Public Shared Function EncodeAddress(addr As String) As Byte()
        Dim _91 = addr(0) = "+"
        If _91 Then
            addr = addr.Substring(1)
        End If
        If addr.Length Mod 2 = 1 Then
            addr += "F"
        End If
        Dim len = addr.Length / 2
        If _91 Then
            len += 1
        End If
        Dim raw(len - 1) As Byte
        Dim offset = 0
        If _91 Then
            raw(0) = &H91
            offset = 1
        End If
        For i = 0 To addr.Length - 1 Step 2
            raw(offset + i / 2) = Convert.ToByte(addr(i), 16) + (Convert.ToByte(addr(i + 1), 16) << 4)
        Next
        Return raw
    End Function
 
    Public Shared Function DecodeAddress(raw As String) As String
        Dim sb As New StringBuilder()
 
        For i = 0 To raw.Length - 1 Step 2
            Dim currblock = raw.Substring(i, 2)
            If i = 0 AndAlso currblock = "91" Then
                sb.Append("+")
            Else
                sb.Append(currblock(1))
                If currblock(0).ToString().ToUpper() <> "F" Then
                    sb.Append(currblock(0))
                End If
            End If
        Next
 
        Return sb.ToString()
    End Function
 
    Public Shared Function DecodeAddress(raw As Byte()) As String
        Return DecodeAddress(BytesToHex(raw))
    End Function
 
 
    Public Shared Function DecodeUCS2(raw As Byte()) As String
        Return Encoding.BigEndianUnicode.GetString(raw)
    End Function
 
    Public Shared Function EncodeUCS2(data As String) As Byte()
        Return Encoding.BigEndianUnicode.GetBytes(data)
    End Function
 
    Public Shared Function Encode7bit(data As String) As Byte()
        Dim raw = Encoding.Default.GetBytes(data)
        Dim rawlen = raw.Length
 
        Dim result(Math.Ceiling(rawlen * 7 / 8 )) As Byte
 
        Dim lastData As Byte = 0 '上一字节残余的数据
        Dim i = 0, counter = 0
 
        '8个字节一组
        Do While i < rawlen
            Dim idInGroup = i Mod 8 '当前正在处理的组内字节的序号,范围是0-7
 
            If idInGroup = 0 Then
                '组内第一个字节,只是保存起来,待处理下一个字节时使用
                lastData = raw(i)
            Else
                '组内其它字节,将其右边部分与残余数据相加,得到一个目标编码字节
                result(counter) = raw(i) << (8 - idInGroup) Or lastData
                lastData = raw(i) >> idInGroup
                counter += 1
            End If
 
            i += 1
        Loop
 
        If i Mod 8 > 0 Then
            result(counter) = lastData
            counter += 1
        End If
 
        ReDim Preserve result(counter - 1)
        Return result
    End Function
 
    Public Shared Function Decode7bit(count As Integer, raw As Byte()) As String
        'Dim raw = HexToByte(data)
        Dim rawlen = raw.Length
 
        Dim result(Math.Ceiling(rawlen * 8 / 7)) As Byte
 
        Dim lastData As Byte = 0 '上一字节残余的数据
        Dim i = 0, counter = 0
 
        '7个字节一组
        Do While i < rawlen
            Dim idInGroup = i Mod 7 '当前正在处理的组内字节的序号,范围是0-6
            result(counter) = (raw(i) << idInGroup Or lastData) And &H7F
            lastData = raw(i) >> (7 - idInGroup)
 
            counter += 1
            i += 1
 
            '到了一组的最后一个字节
            If i Mod 7 = 0 Then
                '额外得到一个目标解码字节
                result(counter) = lastData
                lastData = 0
 
                counter += 1
            End If
        Loop
        ReDim Preserve result(counter - 1)
        Return Encoding.Default.GetString(result)
    End Function
 
    Public Shared Function HexToBytes(data As String) As Byte()
        Dim bin(data.Length / 2 - 1) As Byte
        For i = 0 To data.Length - 1 Step 2
            bin(i / 2) = Convert.ToByte(data.Substring(i, 2), 16)
        Next
        Return bin
    End Function
 
    Public Shared Function BytesToHex(data As Byte()) As String
        Dim sb As New StringBuilder()
        For i = 0 To data.Length - 1
            sb.Append(data(i).ToString("X2"))
        Next
        Return sb.ToString()
    End Function
 
    Public Shared Function HexToByte(data As String) As Byte
        Return Convert.ToByte(data, 16)
    End Function
 
    Public Shared Function BytesToHex(data As Byte) As String
        Return data.ToString("X2")
    End Function
End Class

重构后, 可维护性指数由80成功降为77!

发表评论?

1 条评论。

发表评论

注意 - 你可以用以下 HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

:wink: :twisted: :roll: :oops: :mrgreen: :lol: :idea: :evil: :cry: :arrow: :?: :-| :-x :-o :-P :-D :-? :) :( :!: 8-O 8)

Trackbacks and Pingbacks:

本文链接:https://twd2.me/archives/6264QrCode