Skip to main content

modbus_iiot/core/
modbustelegram.rs

1
2
3use core::consts::*;
4use core::datatransformation::*;
5
6//	===============================================================================================
7
8pub struct ModbusTelegram
9{
10	transaction_identifier : u16,
11	unit_identifier : u8,
12	function_code : u8,
13	payload : Vec< u8 >,
14	expected_bytes : u8
15}
16
17impl ModbusTelegram
18{
19	pub fn new ( transaction_identifier : u16, unit_identifier : u8, function_code : u8, payload : &Vec< u8 >, expected_bytes : u8 ) -> Option< ModbusTelegram >
20	{
21		let reply : Option< ModbusTelegram >;
22
23		if transaction_identifier > 0x0000 && function_code > 0x00
24		{
25			reply = Some(
26				ModbusTelegram
27				{
28					transaction_identifier : transaction_identifier,
29					unit_identifier : unit_identifier,
30					function_code : function_code,
31					payload : payload.clone (),
32					expected_bytes : expected_bytes
33				}
34			);
35		}
36		else
37		{		
38			reply = None;		
39		}
40
41		return reply;
42	}
43
44	pub fn new_from_bytes ( bytes : &Vec< u8 > ) -> Option< ModbusTelegram >
45	{
46		let reply : Option< ModbusTelegram >;
47
48		if bytes.len () > 9
49		{
50			let response_transaction_identifier : Option< u16 > = extract_word_from_bytearray ( &bytes, 
51																								0 );
52			let response_unit_identifier : Option< u8 > = extract_byte_from_bytearray ( &bytes, 
53																						6 );
54			let response_function_code : Option< u8 > =	extract_byte_from_bytearray ( &bytes, 
55																					  7 );
56			let function_code : u8 = response_function_code.unwrap ();
57			let response_payload : Option< Vec< u8 > > = extract_payload_by_function_code ( function_code, 
58																							&bytes );
59
60			if response_transaction_identifier.is_some () &&
61			   response_unit_identifier.is_some () &&
62			   response_payload.is_some ()
63			{
64				reply =	Some(
65					ModbusTelegram
66					{
67						transaction_identifier : response_transaction_identifier.unwrap (),
68						unit_identifier : response_unit_identifier.unwrap (),
69						function_code :	function_code,
70						payload : response_payload.unwrap (),
71						expected_bytes : 0x00
72					}
73				);
74			}
75			else
76			{			
77				reply = None;
78			}
79		}
80		else
81		{
82			reply = None;
83		}
84
85		return reply;
86	}
87
88	pub fn get_bytes ( &self ) -> Option< Vec< u8 > >
89	{
90		let mut reply : Vec< u8 > = vec![];
91
92		let length_for_header : u16 = self.payload.len () as u16 + MODBUS_UNIT_IDENTIFIER_LENGTH + MODBUS_FUNCTION_CODE_LENGTH;
93
94		append_word_to_bytearray ( &mut reply, 
95								   self.transaction_identifier );
96		append_word_to_bytearray ( &mut reply, 
97								   MODBUS_PROTOCOL_IDENTIFIER_TCP );
98		append_word_to_bytearray ( &mut reply, 
99								   length_for_header );
100		append_byte_to_bytearray ( &mut reply, 
101								   self.unit_identifier );
102		append_byte_to_bytearray ( &mut reply, 
103								   self.function_code );
104		append_bytearray_to_bytearray ( &mut reply, 
105										&self.payload );
106
107		return Some( reply );
108	}
109
110	pub fn get_expected_byte_count ( &self ) -> Option< u8 >
111	{
112		let reply : Option< u8 >;
113
114		if self.expected_bytes > MODBUS_HEADER_SIZE
115		{
116			reply = Some( self.expected_bytes );			
117		}
118		else
119		{
120			reply = None;
121		}
122
123		return reply;
124	}
125
126	pub fn get_function_code ( &self ) -> Option< u8 >
127	{
128		let reply : Option< u8 >;
129
130		if self.function_code > 0x00
131		{
132			reply = Some( self.function_code );			
133		}
134		else
135		{
136			reply = None;
137		}
138
139		return reply;
140	}
141
142	pub fn get_payload ( &self ) -> Option< Vec< u8 > >
143	{
144		return Some( self.payload.clone () );
145	}
146}
147
148//	===============================================================================================
149
150#[test]
151fn test_extract_payload_by_function_code ()
152{
153	let mut test_data_1 : Vec< u8 > = vec![];
154	test_data_1.push ( 0x00 ) ;	//	transaction_identifier
155	test_data_1.push ( 0xA0 );	//	transaction_identifier
156	test_data_1.push ( 0x00 );	//	protocol_identifier
157	test_data_1.push ( 0x00 );	//	protocol_identifier
158	test_data_1.push ( 0x00 );	//	length of all following bytes
159	test_data_1.push ( 0x09 );	//	length of all following bytes
160	test_data_1.push ( 0x01 );	//	unit_identifier
161	test_data_1.push ( 0x03 );	//	FUNCTION_CODE_READ_HOLDING_REGISTERS
162	test_data_1.push ( 0x06 );	//	byte count
163	test_data_1.push ( 0xF0 );	//	data
164	test_data_1.push ( 0x0F );	//	data
165	test_data_1.push ( 0x00 );	//	data
166	test_data_1.push ( 0xFF );	//	data
167	test_data_1.push ( 0xFF );	//	data
168	test_data_1.push ( 0x00 );	//	data
169
170	let result_data_1 : Option< Vec< u8 > > = extract_payload_by_function_code ( 0x03, 
171																				 &test_data_1 );
172	assert! ( result_data_1.is_some () );
173
174	let result_bytes_1 : Vec< u8 > = result_data_1.unwrap ();
175	assert_eq! ( result_bytes_1.len (), 7 );
176	assert_eq! ( result_bytes_1[ 0 ], 0x06 );
177	assert_eq! ( result_bytes_1[ 1 ], 0xF0 );
178	assert_eq! ( result_bytes_1[ 2 ], 0x0F );
179	assert_eq! ( result_bytes_1[ 3 ], 0x00 );
180	assert_eq! ( result_bytes_1[ 4 ], 0xFF );
181	assert_eq! ( result_bytes_1[ 5 ], 0xFF );
182	assert_eq! ( result_bytes_1[ 6 ], 0x00 );
183
184	let mut test_data_2 : Vec< u8 > = vec![];
185	test_data_2.push ( 0x00 );	//	transaction_identifier
186	test_data_2.push ( 0xA0 );	//	transaction_identifier
187	test_data_2.push ( 0x00 );	//	protocol_identifier
188	test_data_2.push ( 0x00 );	//	protocol_identifier
189	test_data_2.push ( 0x00 );	//	length of all following bytes
190	test_data_2.push ( 0x06 );	//	length of all following bytes
191	test_data_2.push ( 0x01 );	//	unit_identifier
192	test_data_2.push ( 0x10 );	//	FUNCTION_CODE_WRITE_MULTIPLE_REGISTERS
193	test_data_2.push ( 0x01 );	//	starting address
194	test_data_2.push ( 0x00 );	//	starting address
195	test_data_2.push ( 0x00 );	//	quantity_of_registers
196	test_data_2.push ( 0x10 );	//	quantity_of_registers
197	
198	let result_data_2 : Option< Vec< u8 > > = extract_payload_by_function_code ( 0x10, 
199																				 &test_data_2 );
200	assert! ( result_data_2.is_some () );
201
202	let result_bytes_2 : Vec< u8 > = result_data_2.unwrap ();
203	assert_eq! ( result_bytes_2.len (), 4 );
204	assert_eq! ( result_bytes_2[ 0 ], 0x01 );
205	assert_eq! ( result_bytes_2[ 1 ], 0x00 );
206	assert_eq! ( result_bytes_2[ 2 ], 0x00 );
207	assert_eq! ( result_bytes_2[ 3 ], 0x10 );	
208}
209
210fn extract_payload_by_function_code ( function_code : u8, bytes : &Vec< u8 > ) -> Option< Vec< u8 > >
211{
212	let reply : Option< Vec< u8 > >;
213
214	match function_code
215	{
216		0x01	=> { reply = extract_payload_with_byte_count ( &bytes ); }
217		0x02	=> { reply = extract_payload_with_byte_count ( &bytes ); }
218		0x03	=> { reply = extract_payload_with_byte_count ( &bytes ); }
219		0x04	=> { reply = extract_payload_with_byte_count ( &bytes ); }
220		0x05	=> { reply = extract_payload_without_byte_count ( &bytes ); }
221		0x06	=> { reply = extract_payload_without_byte_count ( &bytes ); }
222		0x0F	=> { reply = extract_payload_without_byte_count ( &bytes ); }
223		0x10	=> { reply = extract_payload_without_byte_count ( &bytes ); }
224		_		=> { reply = None; }
225	}
226
227	return reply;
228}
229
230//	===============================================================================================
231
232#[test]
233fn test_extract_payload_with_byte_count ()
234{
235	let mut test_data : Vec< u8 > = vec![];
236
237	test_data.push ( 0x00 );	//	transaction_identifier
238	test_data.push ( 0xA0 );	//	transaction_identifier
239	test_data.push ( 0x00 );	//	protocol_identifier
240	test_data.push ( 0x00 );	//	protocol_identifier
241	test_data.push ( 0x00 );	//	length of all following bytes
242	test_data.push ( 0x09 );	//	length of all following bytes
243	test_data.push ( 0x01 );	//	unit_identifier
244	test_data.push ( 0x03 );	//	FUNCTION_CODE_READ_HOLDING_REGISTERS
245	test_data.push ( 0x06 );	//	byte count
246	test_data.push ( 0xF0 );	//	data
247	test_data.push ( 0x0F );	//	data
248	test_data.push ( 0x00 );	//	data
249	test_data.push ( 0xFF );	//	data
250	test_data.push ( 0xFF );	//	data
251	test_data.push ( 0x00 );	//	data
252
253	let result_data : Option< Vec< u8 > > = extract_payload_with_byte_count ( &test_data );
254	assert! ( result_data.is_some () );
255
256	let result_bytes : Vec< u8 > = result_data.unwrap ();
257	assert_eq! ( result_bytes.len (), 7 );
258	assert_eq! ( result_bytes[ 0 ], 0x06 );
259	assert_eq! ( result_bytes[ 1 ], 0xF0 );
260	assert_eq! ( result_bytes[ 2 ], 0x0F );
261	assert_eq! ( result_bytes[ 3 ], 0x00 );
262	assert_eq! ( result_bytes[ 4 ], 0xFF );
263	assert_eq! ( result_bytes[ 5 ], 0xFF );
264	assert_eq! ( result_bytes[ 6 ], 0x00 );
265}
266
267fn extract_payload_with_byte_count ( bytes : &Vec< u8 > ) -> Option< Vec< u8 > >
268{
269	let reply : Option< Vec< u8 > >;
270
271	let byte_count : Option< u8 > = extract_byte_from_bytearray ( &bytes, 
272																  8 );
273	let count : u8 = byte_count.unwrap () + 1;
274
275	reply = extract_bytes_from_bytearray ( &bytes, 
276										   8, 
277										   count );
278
279	return reply;
280}
281
282//	===============================================================================================
283
284#[test]
285fn test_extract_payload_without_byte_count ()
286{
287	let mut test_data : Vec< u8 > = vec![];
288
289	test_data.push ( 0x00 ) ;	//	transaction_identifier
290	test_data.push ( 0xA0 );	//	transaction_identifier
291	test_data.push ( 0x00 );	//	protocol_identifier
292	test_data.push ( 0x00 );	//	protocol_identifier
293	test_data.push ( 0x00 );	//	length of all following bytes
294	test_data.push ( 0x06 );	//	length of all following bytes
295	test_data.push ( 0x01 );	//	unit_identifier
296	test_data.push ( 0x10 );	//	FUNCTION_CODE_WRITE_MULTIPLE_REGISTERS
297	test_data.push ( 0x01 );	//	starting address
298	test_data.push ( 0x00 );	//	starting address
299	test_data.push ( 0x00 );	//	quantity_of_registers
300	test_data.push ( 0x10 );	//	quantity_of_registers
301	
302	let result_data : Option< Vec< u8 > > = extract_payload_without_byte_count ( &test_data );
303	assert! ( result_data.is_some () );
304
305	let result_bytes : Vec< u8 > = result_data.unwrap ();
306	assert_eq! ( result_bytes.len (), 4 );
307	assert_eq! ( result_bytes[ 0 ], 0x01 );
308	assert_eq! ( result_bytes[ 1 ], 0x00 );
309	assert_eq! ( result_bytes[ 2 ], 0x00 );
310	assert_eq! ( result_bytes[ 3 ], 0x10 );
311}
312
313fn extract_payload_without_byte_count ( bytes : &Vec< u8 > ) -> Option< Vec< u8 > >
314{
315	let reply : Option< Vec< u8 > >;
316
317	let byte_count : u8 = bytes.len () as u8 - MODBUS_HEADER_SIZE - 1; // -1 for FunctionCode
318
319	reply = extract_bytes_from_bytearray ( &bytes, 
320										   8, 
321										   byte_count );
322
323	return reply;
324}
325
326//	===============================================================================================
327
328#[test]
329fn test_verify_function_code ()
330{
331	let l_payload : Vec< u8 > = vec![ 0x00, 0xFF, 0x00, 0x0A ];
332
333	let test_data_request : Option< ModbusTelegram > = ModbusTelegram::new ( 0x00A0, 
334																			 0x01, 
335																			 0x01, 
336																			 &l_payload, 
337																			 0x00 );
338	assert! ( test_data_request.is_some () );
339	
340	let test_data_response : Option< ModbusTelegram > = ModbusTelegram::new ( 0x00A0, 
341																			  0x01, 
342																			  0x01, 
343																			  &l_payload, 
344																			  0x00 );
345	assert! ( test_data_response.is_some () );
346
347	let is_equal : bool = verify_function_code ( &test_data_request.unwrap (), 
348												 &test_data_response.unwrap () );
349	assert! ( is_equal );
350}
351
352pub fn verify_function_code ( request_telegram : &ModbusTelegram, response_telegram : &ModbusTelegram ) -> bool
353{
354	let mut reply : bool = false;
355
356	if let Some( function_code_request ) = request_telegram.get_function_code ()
357	{
358	    if let Some( function_code_response ) = response_telegram.get_function_code ()
359		{
360			if function_code_request == function_code_response
361			{
362				reply = true;
363			}
364		}
365	}
366
367	return reply;
368}