1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 Ball Aerospace & Technologies Corp.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU Affero General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+
16
+ # Modified by OpenC3, Inc.
17
+ # All changes Copyright 2022, OpenC3, Inc.
18
+ # All Rights Reserved
19
+ #
20
+ # This file may also be used under the terms of a commercial license
21
+ # if purchased from OpenC3, Inc.
22
+
23
+ require 'openc3/config/config_parser'
24
+ require 'openc3/interfaces/protocols/protocol'
25
+ require 'openc3/utilities/crc'
26
+ require 'thread'
27
+
28
+ module OpenC3
29
+ # Creates a CRC on write and verifies a CRC on read
30
+ class CrcModProtocol < Protocol
31
+ ERROR = "ERROR" # on CRC mismatch
32
+ DISCONNECT = "DISCONNECT" # on CRC mismatch
33
+
34
+ # @param write_item_name [String/nil] Item to fill with calculated CRC value for outgoing packets (nil = don't fill)
35
+ # @param strip_crc [Boolean] Whether or not to remove the CRC from incoming packets
36
+ # @param bad_strategy [ERROR/DISCONNECT] How to handle CRC errors on incoming packets. ERROR = Just log the error, DISCONNECT = Disconnect interface
37
+ # @param bit_offset [Integer] Bit offset of the CRC in the data. Can be negative to indicate distance from end of packet
38
+ # @param bit_size [Integer] Bit size of the CRC - Must be 16, 32, or 64
39
+ # @param endianness [BIG_ENDIAN/LITTLE_ENDIAN] Endianness of the CRC
40
+ # @param poly [Integer] Polynomial to use when calculating the CRC
41
+ # @param seed [Integer] Seed value to start the calculation
42
+ # @param xor [Boolean] Whether to XOR the CRC result with 0xFFFF
43
+ # @param reflect [Boolean] Whether to bit reverse each byte of data before calculating the CRC
44
+ # @param allow_empty_data [true/false/nil] See Protocol#initialize
45
+ def initialize (
46
+ write_item_name = nil ,
47
+ strip_crc = false ,
48
+ bad_strategy = "ERROR" ,
49
+ bit_offset = -32 ,
50
+ bit_size = 32 ,
51
+ endianness = 'BIG_ENDIAN' ,
52
+ poly = nil ,
53
+ seed = nil ,
54
+ xor = nil ,
55
+ reflect = nil ,
56
+ allow_empty_data = nil
57
+ )
58
+ super ( allow_empty_data )
59
+ @write_item_name = ConfigParser . handle_nil ( write_item_name )
60
+ @strip_crc = ConfigParser . handle_true_false ( strip_crc )
61
+ raise "Invalid strip CRC of '#{ strip_crc } '. Must be TRUE or FALSE." unless !!@strip_crc == @strip_crc
62
+
63
+ case bad_strategy
64
+ when ERROR , DISCONNECT
65
+ @bad_strategy = bad_strategy
66
+ else
67
+ raise "Invalid bad CRC strategy of #{ bad_strategy } . Must be ERROR or DISCONNECT."
68
+ end
69
+
70
+ case endianness . to_s . upcase
71
+ when 'BIG_ENDIAN'
72
+ @endianness = :BIG_ENDIAN # Convert to symbol for use in BinaryAccessor.write
73
+ when 'LITTLE_ENDIAN'
74
+ @endianness = :LITTLE_ENDIAN # Convert to symbol for use in BinaryAccessor.write
75
+ else
76
+ raise "Invalid endianness '#{ endianness } '. Must be BIG_ENDIAN or LITTLE_ENDIAN."
77
+ end
78
+
79
+ begin
80
+ @bit_offset = Integer ( bit_offset )
81
+ rescue
82
+ raise "Invalid bit offset of #{ bit_offset } . Must be a number."
83
+ end
84
+ raise "Invalid bit offset of #{ bit_offset } . Must be divisible by 8." if @bit_offset % 8 != 0
85
+
86
+ poly = ConfigParser . handle_nil ( poly )
87
+ begin
88
+ poly = Integer ( poly ) if poly
89
+ rescue
90
+ raise "Invalid polynomial of #{ poly } . Must be a number."
91
+ end
92
+
93
+ seed = ConfigParser . handle_nil ( seed )
94
+ begin
95
+ seed = Integer ( seed ) if seed
96
+ rescue
97
+ raise "Invalid seed of #{ seed } . Must be a number."
98
+ end
99
+
100
+ xor = ConfigParser . handle_true_false_nil ( xor )
101
+ raise "Invalid XOR value of '#{ xor } '. Must be TRUE or FALSE." if xor && !!xor != xor
102
+
103
+ reflect = ConfigParser . handle_true_false_nil ( reflect ) if reflect
104
+ raise "Invalid reflect value of '#{ reflect } '. Must be TRUE or FALSE." if reflect && !!reflect != reflect
105
+
106
+ # Built the CRC arguments array. All subsequent arguments are dependent
107
+ # on the previous ones so we build it up incrementally.
108
+ args = [ ]
109
+ if poly
110
+ args << poly
111
+ if seed
112
+ args << seed
113
+ unless xor . nil? # Can't check raw variable because it could be false
114
+ args << xor
115
+ unless reflect . nil? # Can't check raw variable because it could be false
116
+ args << reflect
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ @bit_size = bit_size . to_i
123
+ case @bit_size
124
+ when 16
125
+ @pack = ( @endianness == :BIG_ENDIAN ) ? 'n' : 'v'
126
+ if args . empty?
127
+ @crc = Crc16 . new
128
+ else
129
+ @crc = Crc16 . new ( *args )
130
+ end
131
+ when 32
132
+ @pack = ( @endianness == :BIG_ENDIAN ) ? 'N' : 'V'
133
+ if args . empty?
134
+ @crc = Crc32 . new
135
+ else
136
+ @crc = Crc32 . new ( *args )
137
+ end
138
+ when 64
139
+ @pack = ( @endianness == :BIG_ENDIAN ) ? 'N' : 'V'
140
+ if args . empty?
141
+ @crc = Crc64 . new
142
+ else
143
+ @crc = Crc64 . new ( *args )
144
+ end
145
+ else
146
+ raise "Invalid bit size of #{ bit_size } . Must be 16, 32, or 64."
147
+ end
148
+ end
149
+
150
+ def read_data ( data , extra = nil )
151
+ return super ( data , extra ) if data . length <= 0
152
+ data . slice! ( 0 , 4 ) # only for serial, remove sync word
153
+ crc = BinaryAccessor . read ( @bit_offset , @bit_size , :UINT , data , @endianness )
154
+ calculated_crc = @crc . calc ( data [ 0 ...( @bit_offset / 8 ) ] )
155
+ if calculated_crc != crc
156
+ Logger . error "#{ @interface ? @interface . name : "" } : Invalid CRC detected! Calculated 0x#{ calculated_crc . to_s ( 16 ) . upcase } vs found 0x#{ crc . to_s ( 16 ) . upcase } ."
157
+ if @bad_strategy == DISCONNECT
158
+ return :DISCONNECT
159
+ end
160
+ end
161
+ if @strip_crc
162
+ new_data = data . dup
163
+ new_data = new_data [ 0 ...( @bit_offset / 8 ) ]
164
+ end_range = ( @bit_offset + @bit_size ) / 8
165
+ new_data << data [ end_range ..-1 ] if end_range != 0
166
+ return new_data , extra
167
+ end
168
+ return data , extra
169
+ end
170
+
171
+ def write_packet ( packet )
172
+ if @write_item_name
173
+ end_range = packet . get_item ( @write_item_name ) . bit_offset / 8
174
+ crc = @crc . calc ( packet . buffer ( false ) [ 0 ...end_range ] )
175
+ packet . write ( @write_item_name , crc )
176
+ end
177
+ packet
178
+ end
179
+
180
+ def write_data ( data , extra = nil )
181
+ unless @write_item_name
182
+ if @bit_size == 64
183
+ crc = @crc . calc ( data )
184
+ data << ( "\x00 " * 8 )
185
+ BinaryAccessor . write ( ( crc >> 32 ) , -64 , 32 , :UINT , data , @endianness , :ERROR )
186
+ BinaryAccessor . write ( ( crc & 0xFFFFFFFF ) , -32 , 32 , :UINT , data , @endianness , :ERROR )
187
+ else
188
+ crc = @crc . calc ( data )
189
+ data << ( "\x00 " * ( @bit_size / 8 ) )
190
+ BinaryAccessor . write ( crc , -@bit_size , @bit_size , :UINT , data , @endianness , :ERROR )
191
+ end
192
+ end
193
+ return data , extra
194
+ end
195
+ end
196
+ end
0 commit comments