mip_cmdqueue.c
Go to the documentation of this file.
1 
2 #include "mip_cmdqueue.h"
3 
4 #include "mip_field.h"
5 #include "mip_packet.h"
6 
7 #include <string.h>
8 #include <assert.h>
9 
10 
11 #define MIP_REPLY_DESC_GLOBAL_ACK_NACK 0xF1
12 
13 #define MIP_INDEX_REPLY_DESCRIPTOR 0
14 #define MIP_INDEX_REPLY_ACK_CODE 1
15 
16 
26 void mip_pending_cmd_init(mip_pending_cmd* cmd, uint8_t descriptor_set, uint8_t field_descriptor)
27 {
28  mip_pending_cmd_init_full(cmd, descriptor_set, field_descriptor, 0x00, NULL, 0, 0);
29 }
30 
42 void mip_pending_cmd_init_with_timeout(mip_pending_cmd* cmd, uint8_t descriptor_set, uint8_t field_descriptor, timeout_type additional_time)
43 {
44  mip_pending_cmd_init_full(cmd, descriptor_set, field_descriptor, 0x00, NULL, 0, additional_time);
45 }
46 
64 void mip_pending_cmd_init_with_response(mip_pending_cmd* cmd, uint8_t descriptor_set, uint8_t field_descriptor, uint8_t response_descriptor, uint8_t* response_buffer, uint8_t response_buffer_size)
65 {
66  mip_pending_cmd_init_full(cmd, descriptor_set, field_descriptor, response_descriptor, response_buffer, response_buffer_size, 0);
67 }
68 
86 void mip_pending_cmd_init_full(mip_pending_cmd* cmd, uint8_t descriptor_set, uint8_t field_descriptor, uint8_t response_descriptor, uint8_t* response_buffer, uint8_t response_buffer_size, timeout_type additional_time)
87 {
88  cmd->_next = NULL;
89  cmd->_response_buffer = NULL;
90  cmd->_extra_timeout = additional_time;
91  cmd->_descriptor_set = descriptor_set;
92  cmd->_field_descriptor = field_descriptor;
93  cmd->_response_descriptor = response_descriptor;
94  cmd->_response_buffer = response_buffer;
95  cmd->_response_buffer_size = response_buffer_size;
96  // cmd->_ack_code = 0xFF; // invalid
97  cmd->_status = MIP_STATUS_NONE;
98 }
99 
100 
107 {
108  return cmd->_status;
109 }
110 
116 const uint8_t* mip_pending_cmd_response(const mip_pending_cmd* cmd)
117 {
118  assert(mip_cmd_result_is_finished(cmd->_status));
119 
120  return cmd->_response_buffer;
121 }
122 
131 {
132  assert(mip_cmd_result_is_finished(cmd->_status));
133 
134  return cmd->_response_length;
135 }
136 
153 {
154  assert(cmd->_status == MIP_STATUS_WAITING);
155 
156  // result <= 0 if timed out.
157  // Note: this still works with unsigned overflow.
158  return (int)(now - cmd->_timeout_time);
159 }
160 
171 {
172  if( cmd->_status == MIP_STATUS_WAITING )
173  {
174  if( mip_pending_cmd_remaining_time(cmd, now) > 0 )
175  {
176  return true;
177  }
178  }
179 
180  return false;
181 }
182 
183 
194 void mip_cmd_queue_init(mip_cmd_queue* queue, timeout_type base_reply_timeout)
195 {
196  queue->_first_pending_cmd = NULL;
197  queue->_base_timeout = base_reply_timeout;
198 
199  MIP_DIAG_ZERO(queue->_diag_cmds_queued);
200  MIP_DIAG_ZERO(queue->_diag_cmds_acked);
201  MIP_DIAG_ZERO(queue->_diag_cmds_nacked);
202  MIP_DIAG_ZERO(queue->_diag_cmds_timedout);
203  MIP_DIAG_ZERO(queue->_diag_cmds_failed);
204 }
205 
216 {
217  // For now only one command can be queued at a time.
218  if( queue->_first_pending_cmd )
219  {
221  return;
222  }
223 
224  MIP_DIAG_INC(queue->_diag_cmds_queued, 1);
225 
227  queue->_first_pending_cmd = cmd;
228 }
229 
239 {
240  if( queue->_first_pending_cmd == cmd )
241  {
242  queue->_first_pending_cmd = NULL;
244  }
245 }
246 
262 static enum mip_cmd_result process_fields_for_pending_cmd(mip_pending_cmd* pending, const mip_packet* packet, timeout_type base_timeout, timestamp_type timestamp)
263 {
264  assert( pending->_status != MIP_STATUS_NONE ); // pending->_status must be set to MIP_STATUS_PENDING in mip_cmd_queue_enqueue to get here.
265  assert( !mip_cmd_result_is_finished(pending->_status) ); // Command shouldn't be finished yet - make sure the queue is processed properly.
266 
267  if( pending->_status == MIP_STATUS_PENDING )
268  {
269  // Update the timeout to the timestamp of the timeout time.
270  pending->_timeout_time = timestamp + base_timeout + pending->_extra_timeout;
271  pending->_status = MIP_STATUS_WAITING;
272  }
273 
274  // ------+------+------+------+------+------+------+------+------+------------------------
275  // ... | 0x02 | 0xF1 | cmd1 | nack | 0x02 | 0xF1 | cmd2 | ack | response field ...
276  // ------+------+------+------+------+------+------+------+------+------------------------
277 
278  if( mip_packet_descriptor_set(packet) == pending->_descriptor_set )
279  {
280  mip_field field = {0};
281  while( mip_field_next_in_packet(&field, packet) )
282  {
283  // Not an ack/nack reply field, skip it.
285  continue;
286 
287  // Sanity check payload length before accessing it.
288  if( mip_field_payload_length(&field) != 2 )
289  continue;
290 
291  const uint8_t* const payload = mip_field_payload(&field);
292 
293  const uint8_t cmd_descriptor = payload[MIP_INDEX_REPLY_DESCRIPTOR];
294  const uint8_t ack_code = payload[MIP_INDEX_REPLY_ACK_CODE];
295 
296  // Is this the right command reply?
297  if( pending->_field_descriptor != cmd_descriptor )
298  continue;
299 
300  // Descriptor matches!
301 
302  uint8_t response_length = 0;
303  mip_field response_field;
304 
305  // If the command was ACK'd, check if response data is expected.
306  if( pending->_response_descriptor != 0x00 && ack_code == MIP_ACK_OK )
307  {
308  // Look ahead one field for response data.
309  response_field = mip_field_next_after(&field);
310  if( mip_field_is_valid(&response_field) )
311  {
312  const uint8_t response_descriptor = mip_field_field_descriptor(&response_field);
313 
314  // This is a wildcard to accept any response data descriptor.
315  // Needed when the response descriptor is not known or is wrong.
317  pending->_response_descriptor = response_descriptor;
318 
319  // Make sure the response descriptor matches what is expected.
320  if( response_descriptor == pending->_response_descriptor )
321  {
322  // Update the response_size field to reflect the actual size.
323  response_length = mip_field_payload_length(&response_field);
324 
325  // Skip this field when iterating for next ack/nack reply.
326  field = response_field;
327  }
328  }
329  }
330 
331  // Limit response data size to lesser of buffer size or actual response length.
332  pending->_response_length = (response_length < pending->_response_buffer_size) ? response_length : pending->_response_buffer_size;
333 
334  // Copy response data to the pending buffer (skip if response_field is invalid).
335  if( pending->_response_length > 0 )
336  memcpy(pending->_response_buffer, mip_field_payload(&response_field), pending->_response_length);
337 
338  // pending->_ack_code = ack_code;
339  pending->_reply_time = timestamp; // Completion time
340 
341  return (enum mip_cmd_result)ack_code;
342  }
343  }
344 
345  // No matching reply descriptors in this packet.
346 
347  // Check for timeout
348  if( mip_pending_cmd_check_timeout(pending, timestamp) )
349  {
350  pending->_response_length = 0;
351  // pending->_ack_code = MIP_NACK_COMMAND_TIMEOUT;
352 
353  // Must be last!
354  return MIP_STATUS_TIMEDOUT;
355  }
356 
357  return pending->_status;
358 }
359 
370 {
371  // Check if the packet is a command descriptor set.
372  const uint8_t descriptor_set = mip_packet_descriptor_set(packet);
373  if( descriptor_set >= 0x80 && descriptor_set < 0xF0 )
374  return;
375 
376  if( queue->_first_pending_cmd )
377  {
378  mip_pending_cmd* pending = queue->_first_pending_cmd;
379 
380  const enum mip_cmd_result status = process_fields_for_pending_cmd(pending, packet, queue->_base_timeout, timestamp);
381 
382  if( mip_cmd_result_is_finished(status) )
383  {
385 
386  // This must be done last b/c it may trigger the thread which queued the command.
387  // The command could go out of scope or its attributes inspected.
388  pending->_status = status;
389 
390 #ifdef MIP_ENABLE_DIAGNOSTICS
391  if( mip_cmd_result_is_ack(status) )
392  MIP_DIAG_INC(queue->_diag_cmds_acked, 1);
393  else if( mip_cmd_result_is_reply(status) )
394  MIP_DIAG_INC(queue->_diag_cmds_nacked, 1);
395  else if( status == MIP_STATUS_TIMEDOUT )
396  MIP_DIAG_INC(queue->_diag_cmds_timedout, 1);
397  else
398  MIP_DIAG_INC(queue->_diag_cmds_failed, 1);
399 #endif // MIP_ENABLE_DIAGNOSTICS
400  }
401  }
402 }
403 
412 {
413  while( queue->_first_pending_cmd )
414  {
415  mip_pending_cmd* pending = queue->_first_pending_cmd;
416  queue->_first_pending_cmd = pending->_next;
417 
418  // This may deallocate the pending command in another thread (make sure to fetch the next cmd first).
419  pending->_status = MIP_STATUS_CANCELLED;
420  }
421 }
422 
435 {
436  if( queue->_first_pending_cmd )
437  {
438  mip_pending_cmd* pending = queue->_first_pending_cmd;
439 
440  if( pending->_status == MIP_STATUS_PENDING )
441  {
442  // Update the timeout to the timestamp of the timeout time.
443  pending->_timeout_time = now + queue->_base_timeout + pending->_extra_timeout;
444  pending->_status = MIP_STATUS_WAITING;
445  }
446  else if( mip_pending_cmd_check_timeout(pending, now) )
447  {
449 
450  // Clear response length and mark when it timed out.
451  pending->_response_length = 0;
452  pending->_reply_time = now;
453 
454  // This must be last!
455  pending->_status = MIP_STATUS_TIMEDOUT;
456 
457  MIP_DIAG_INC(queue->_diag_cmds_timedout, 1);
458  }
459  }
460 }
461 
472 {
473  queue->_base_timeout = timeout;
474 }
475 
482 {
483  return queue->_base_timeout;
484 }
485 
486 
487 #ifdef MIP_ENABLE_DIAGNOSTICS
488 
494 uint16_t mip_cmd_queue_diagnostic_cmds_queued(const mip_cmd_queue* queue)
495 {
496  return queue->_diag_cmds_queued;
497 }
498 
502 uint16_t mip_cmd_queue_diagnostic_cmds_failed(const mip_cmd_queue* queue)
503 {
504  return (uint16_t)queue->_diag_cmds_nacked + queue->_diag_cmds_failed + queue->_diag_cmds_timedout;
505 }
506 
512 uint16_t mip_cmd_queue_diagnostic_cmds_successful(const mip_cmd_queue* queue)
513 {
514  return queue->_diag_cmds_acked;
515 }
516 
522 uint16_t mip_cmd_queue_diagnostic_cmd_acks(const mip_cmd_queue* queue)
523 {
524  return queue->_diag_cmds_acked;
525 }
526 
530 uint16_t mip_cmd_queue_diagnostic_cmd_nacks(const mip_cmd_queue* queue)
531 {
532  return queue->_diag_cmds_nacked;
533 }
534 
539 uint16_t mip_cmd_queue_diagnostic_cmd_timeouts(const mip_cmd_queue* queue)
540 {
541  return queue->_diag_cmds_timedout;
542 }
543 
547 uint16_t mip_cmd_queue_diagnostic_cmd_errors(const mip_cmd_queue* queue)
548 {
549  return queue->_diag_cmds_failed;
550 }
551 
552 #endif // MIP_ENABLE_DIAGNOSTICS
timeout_type _base_timeout
Definition: mip_cmdqueue.h:96
uint8_t _descriptor_set
Command descriptor set.
Definition: mip_cmdqueue.h:50
timeout_type _extra_timeout
<
Definition: mip_cmdqueue.h:46
Holds a list of pending commands.
Definition: mip_cmdqueue.h:93
void mip_cmd_queue_update(mip_cmd_queue *queue, timestamp_type now)
Call periodically to make sure commands time out if no packets are received.
Definition: mip_cmdqueue.c:434
enum mip_cmd_result mip_pending_cmd_status(const mip_pending_cmd *cmd)
Returns the status of the pending command.
Definition: mip_cmdqueue.c:106
bool mip_field_is_valid(const mip_field *field)
Returns true if the field has a valid field descriptor.
Definition: mip_field.c:78
int mip_pending_cmd_remaining_time(const mip_pending_cmd *cmd, timestamp_type now)
Determines how much time is remaining before the command times out.
Definition: mip_cmdqueue.c:152
void mip_cmd_queue_clear(mip_cmd_queue *queue)
Clears the command queue.
Definition: mip_cmdqueue.c:411
bool mip_cmd_result_is_ack(enum mip_cmd_result result)
Determines if the result is an ack (successful response from the device)
Definition: mip_result.c:67
mip_field mip_field_next_after(const mip_field *field)
Gets the next field after the specified field._.
Definition: mip_field.c:179
void mip_cmd_queue_init(mip_cmd_queue *queue, timeout_type base_reply_timeout)
Initializes a command queue.
Definition: mip_cmdqueue.c:194
Waiting for command reply (timeout timer has started).
Definition: mip_result.h:28
bool mip_pending_cmd_check_timeout(const mip_pending_cmd *cmd, timestamp_type now)
Checks if the command should time out.
Definition: mip_cmdqueue.c:170
timeout_type mip_cmd_queue_base_reply_timeout(const mip_cmd_queue *queue)
Gets the base reply timeout for all commands.
Definition: mip_cmdqueue.c:481
mip_pending_cmd * _first_pending_cmd
Definition: mip_cmdqueue.h:95
uint8_t _response_length
If status == MIP_STATUS_COMPLETED, the length of the reply data.
Definition: mip_cmdqueue.h:55
#define MIP_INDEX_REPLY_DESCRIPTOR
Definition: mip_cmdqueue.c:13
timestamp_type timeout_type
Definition: mip_types.h:35
uint8_t _response_buffer_size
If status < MIP_STATUS_COMPLETED, the size of the reply data buffer.
Definition: mip_cmdqueue.h:54
enum mip_cmd_result _status
The current status of the command. Writing this to any MipAck value may cause deallocation.
Definition: mip_cmdqueue.h:57
uint8_t mip_pending_cmd_response_length(const mip_pending_cmd *cmd)
Returns the length of the response data.
Definition: mip_cmdqueue.c:130
void mip_cmd_queue_set_base_reply_timeout(mip_cmd_queue *queue, timeout_type timeout)
Sets the base reply timeout for all commands.
Definition: mip_cmdqueue.c:471
status
mip_cmd_result
Represents the status of a MIP command.
Definition: mip_result.h:23
const uint8_t * mip_pending_cmd_response(const mip_pending_cmd *cmd)
Returns the response payload pointer.
Definition: mip_cmdqueue.c:116
uint8_t _response_descriptor
Response field descriptor, or 0x00 if no response field expected.
Definition: mip_cmdqueue.h:52
Structure representing a MIP Packet.
Definition: mip_packet.h:43
void mip_pending_cmd_init_with_timeout(mip_pending_cmd *cmd, uint8_t descriptor_set, uint8_t field_descriptor, timeout_type additional_time)
Initialize a pending mip commmand with extra timeout time.
Definition: mip_cmdqueue.c:42
Represents a command awaiting a reply from the device.
Definition: mip_cmdqueue.h:41
uint8_t mip_field_field_descriptor(const mip_field *field)
Returns the field descriptor.
Definition: mip_field.c:52
uint8_t mip_field_payload_length(const mip_field *field)
Returns the length of the payload.
Definition: mip_field.c:60
uint8_t * _response_buffer
Buffer for response data if response_descriptor != 0x00.
Definition: mip_cmdqueue.h:44
static enum mip_cmd_result process_fields_for_pending_cmd(mip_pending_cmd *pending, const mip_packet *packet, timeout_type base_timeout, timestamp_type timestamp)
Iterate over a packet, checking for replies to the pending command.
Definition: mip_cmdqueue.c:262
struct mip_pending_cmd * _next
Next command in the queue.
Definition: mip_cmdqueue.h:43
void mip_cmd_queue_process_packet(mip_cmd_queue *queue, const mip_packet *packet, timestamp_type timestamp)
Process an incoming packet and check for replies to pending commands.
Definition: mip_cmdqueue.c:369
A structure representing a MIP field.
Definition: mip_field.h:52
bool mip_cmd_result_is_reply(enum mip_cmd_result result)
Determines if the result is a reply from the device (i.e.
Definition: mip_result.c:51
#define MIP_DIAG_INC(counter, amount)
Definition: mip_types.h:48
#define MIP_INDEX_REPLY_ACK_CODE
Definition: mip_cmdqueue.c:14
void mip_pending_cmd_init_with_response(mip_pending_cmd *cmd, uint8_t descriptor_set, uint8_t field_descriptor, uint8_t response_descriptor, uint8_t *response_buffer, uint8_t response_buffer_size)
Initialize a pending mip commmand with expected response data.
Definition: mip_cmdqueue.c:64
Reply was not received before timeout expired.
Definition: mip_result.h:27
timestamp_type _timeout_time
If MIP_STATUS_WAITING: timestamp_type after which the command will be timed out.
Definition: mip_cmdqueue.h:47
bool mip_field_next_in_packet(mip_field *field, const mip_packet *packet)
Iterates over all fields in a packet.
Definition: mip_field.c:224
void mip_cmd_queue_enqueue(mip_cmd_queue *queue, mip_pending_cmd *cmd)
Queue a command to wait for replies.
Definition: mip_cmdqueue.c:215
#define MIP_DIAG_ZERO(counter)
Definition: mip_types.h:50
Command has been queued but the I/O update hasn&#39;t run yet.
Definition: mip_result.h:29
Command has been initialized but not queued yet.
Definition: mip_result.h:30
const uint8_t * mip_field_payload(const mip_field *field)
Returns the payload pointer for the field data.
Definition: mip_field.c:70
uint8_t _field_descriptor
Command field descriptor.
Definition: mip_cmdqueue.h:51
uint64_t timestamp_type
Type used for packet timestamps and timeouts.
Definition: mip_types.h:32
Command was canceled in software.
Definition: mip_result.h:26
bool mip_cmd_result_is_finished(enum mip_cmd_result status)
Determines if the command has completed, timed out, been cancelled, or otherwise is no longer waiting...
Definition: mip_result.c:43
uint8_t mip_packet_descriptor_set(const mip_packet *packet)
Returns the MIP descriptor set for this packet.
Definition: mip_packet.c:87
Command completed successfully.
Definition: mip_result.h:32
void mip_cmd_queue_dequeue(mip_cmd_queue *queue, mip_pending_cmd *cmd)
Removes a pending command from the queue.
Definition: mip_cmdqueue.c:238
void mip_pending_cmd_init_full(mip_pending_cmd *cmd, uint8_t descriptor_set, uint8_t field_descriptor, uint8_t response_descriptor, uint8_t *response_buffer, uint8_t response_buffer_size, timeout_type additional_time)
Initialize a pending mip commmand with all parameters.
Definition: mip_cmdqueue.c:86
timestamp_type _reply_time
If MIP_STATUS_COMPLETED: timestamp_type from the packet containing the ack/nack.
Definition: mip_cmdqueue.h:48
#define MIP_REPLY_DESC_GLOBAL_ACK_NACK
Definition: mip_cmdqueue.c:11
void mip_pending_cmd_init(mip_pending_cmd *cmd, uint8_t descriptor_set, uint8_t field_descriptor)
Initialize a pending command with no reponse data or additional time.
Definition: mip_cmdqueue.c:26


microstrain_inertial_driver
Author(s): Brian Bingham, Parker Hannifin Corp
autogenerated on Wed Mar 22 2023 02:35:06