Index: firmware/App/Services/SystemComm.c =================================================================== diff -u -rf7e3018ec6ab762fe08efb42b21fb2ca970174b0 -r1375b56bfeb3f458644484d9fe4f2e75e1289e38 --- firmware/App/Services/SystemComm.c (.../SystemComm.c) (revision f7e3018ec6ab762fe08efb42b21fb2ca970174b0) +++ firmware/App/Services/SystemComm.c (.../SystemComm.c) (revision 1375b56bfeb3f458644484d9fe4f2e75e1289e38) @@ -25,7 +25,7 @@ #include "SystemComm.h" #include "Comm.h" #include "Interrupts.h" -#include "MsgQueues.h" +#include "Timers.h" #include "Utilities.h" #include "SystemCommMessages.h" @@ -42,6 +42,31 @@ #define SCI1_RECEIVE_DMA_REQUEST 30 #define SCI1_TRANSMIT_DMA_REQUEST 31 +#define UI_COMM_TIMEOUT_IN_MS 5000 +#define DG_COMM_TIMEOUT_IN_MS 2000 + +#define MAX_COMM_CRC_FAILURES 5 +#define MAX_COMM_CRC_FAILURE_WINDOW_MS (10 * SEC_PER_MIN * MS_PER_SECOND) + +#define MSG_NOT_ACKED_TIMEOUT_MS ( MS_PER_SECOND * 1 ) +#define MSG_NOT_ACKED_MAX_RETRIES 3 +#define PENDING_ACK_LIST_SIZE 25 + +#pragma pack(push,1) + +typedef struct +{ + BOOL used; + U16 seqNo; + U16 retries; + U32 timeStamp; + COMM_BUFFER_T channel; + U32 msgSize; + U08 msg[ MAX_ACK_MSG_SIZE ]; +} PENDING_ACK_RECORD_T; + +#pragma pack(pop) + // ********** private data ********** const COMM_BUFFER_T CAN_OUT_BUFFERS[ NUM_OF_CAN_OUT_BUFFERS ] = @@ -68,14 +93,24 @@ static U08 pcXmitPacket[ PC_MESSAGE_PACKET_SIZE ] = { 0, 0, 0, 0, 0, 0, 0, 0 }; static U08 pcRecvPacket[ PC_MESSAGE_PACKET_SIZE ] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +#ifndef ACK_NOT_IMPLEMENTED +static PENDING_ACK_RECORD_T pendingAckList[ PENDING_ACK_LIST_SIZE ]; // list of outgoing messages that are awaiting an ACK +#endif + // DMA control records -static g_dmaCTRL pcDMAXmitControlRecord; // DMA transmit control record (UART-debug) -static g_dmaCTRL pcDMARecvControlRecord; // DMA receive control record (UART-debug) +static g_dmaCTRL pcDMAXmitControlRecord; // DMA transmit control record (UART-debug) +static g_dmaCTRL pcDMARecvControlRecord; // DMA receive control record (UART-debug) -static volatile BOOL dgIsCommunicating = FALSE; // has DG sent a message since last check -static volatile BOOL uiIsCommunicating = FALSE; // has UI sent a message since last check -static volatile BOOL uiDidCommunicate = FALSE; // has UI every sent a message +static volatile BOOL dgIsCommunicating = FALSE; // has DG sent a message since last check +static U32 timeOfLastDGCheckIn = 0; // last time DG checked in +static volatile BOOL uiIsCommunicating = FALSE; // has UI sent a message since last check +static U32 timeOfLastUICheckIn = 0; // last time UI checked in +static volatile BOOL uiDidCommunicate = FALSE; // has UI every sent a message +static U32 badCRCTimeStamps[ MAX_COMM_CRC_FAILURES ]; // time of last five bad message CRCs (wrapping list) +static U32 badCRCListIdx = 0; // where next bad message CRC time stamp will go in list +static U32 badCRCListCount = 0; // # of bad CRCs in the list + // ********** private function prototypes ********** static void initUARTAndDMA( void ); @@ -92,6 +127,14 @@ static void processReceivedMessages( void ); static void processReceivedMessage( MESSAGE_T *message ); +static void checkForCommTimeouts( void ); +static void checkTooManyBadMsgCRCs( U16 msgID ); + +#ifndef ACK_NOT_IMPLEMENTED +static BOOL matchACKtoPendingACKList( S16 seqNo ); +static void checkPendingACKList( void ); +#endif + /************************************************************************* * @brief initSystemComm * The initSystemComm function initializes the SystemComm module. @@ -103,8 +146,24 @@ *************************************************************************/ void initSystemComm( void ) { + U32 i; + // initialize UART and DMA for PC communication initUARTAndDMA(); + + // initialize bad message CRC list + for ( i = 0; i < MAX_COMM_CRC_FAILURES; i++ ) + { + badCRCTimeStamps[ i ] = 0; + } + +#ifndef ACK_NOT_IMPLEMENTED + // initialize pending ACK list + for ( i = 0; i < PENDING_ACK_LIST_SIZE; i++ ) + { + pendingAckList[ i ].used = FALSE; + } +#endif } /************************************************************************* @@ -120,6 +179,7 @@ void checkInFromDG( void ) { dgIsCommunicating = TRUE; + timeOfLastDGCheckIn = getMSTimerCount(); } /************************************************************************* @@ -135,6 +195,7 @@ void checkInFromUI( void ) { uiIsCommunicating = TRUE; + timeOfLastUICheckIn = getMSTimerCount(); uiDidCommunicate = TRUE; } @@ -206,6 +267,14 @@ // process received messages in the queue processReceivedMessages(); + + // check for sub-system comm timeouts + checkForCommTimeouts(); + +#ifndef ACK_NOT_IMPLEMENTED + // check ACK list for messages that need to be re-sent because they haven't been ACK'd + checkPendingACKList(); +#endif } /************************************************************************* @@ -593,28 +662,25 @@ if ( msgSize > 0 ) { + MESSAGE_WRAPPER_T rcvMsg; + U08 *dataPtr = data+1; // skip over sync byte + // consume message (+sync byte) msgSize = getFromCommBuffer( MSG_IN_BUFFERS[ i ], data, msgSize + 1 ); - // if message data is at least minimum size, convert received message data to a message and add to message queue - if ( msgSize > MESSAGE_OVERHEAD_SIZE ) - { - MESSAGE_WRAPPER_T rcvMsg; - U08 *dataPtr = data+1; // skip over sync byte - - messagesInBuffer = TRUE; - // blank the new message record - blankMessageInWrapper( &rcvMsg ); - // copy message header portion of message data to the new message - memcpy( &(rcvMsg.msg.hdr), dataPtr, sizeof(MESSAGE_HEADER_T) ); - dataPtr += sizeof(MESSAGE_HEADER_T); - // copy message payload portion of message data to the new message - memcpy( &(rcvMsg.msg.payload), dataPtr, rcvMsg.msg.hdr.payloadLen ); - dataPtr += rcvMsg.msg.hdr.payloadLen; - // copy CRC portion of message data to the new message - rcvMsg.crc = *dataPtr; - // add new message to queue for later processing - addToMsgQueue( MSG_Q_IN, &rcvMsg ); - } // message is at least as large as minimum size + // convert received message data to a message and add to message queue + messagesInBuffer = TRUE; + // blank the new message record + blankMessageInWrapper( &rcvMsg ); + // copy message header portion of message data to the new message + memcpy( &(rcvMsg.msg.hdr), dataPtr, sizeof(MESSAGE_HEADER_T) ); + dataPtr += sizeof(MESSAGE_HEADER_T); + // copy message payload portion of message data to the new message + memcpy( &(rcvMsg.msg.payload), dataPtr, rcvMsg.msg.hdr.payloadLen ); + dataPtr += rcvMsg.msg.hdr.payloadLen; + // copy CRC portion of message data to the new message + rcvMsg.crc = *dataPtr; + // add new message to queue for later processing + addToMsgQueue( MSG_Q_IN, &rcvMsg ); } // looks like there is a complete message in the comm buffer } // enough data left in comm buffer to possibly be a complete message } // while loop to get all complete messages for each comm buffer @@ -681,7 +747,7 @@ // if a minimum sized msg would fit in remaining, continue if ( remSize >= MESSAGE_OVERHEAD_SIZE ) { - payloadSize = data[ pos + sizeof( U16 ) ]; + payloadSize = data[ pos + sizeof(MESSAGE_HEADER_T) - sizeof(U08) ]; msgSize = MESSAGE_OVERHEAD_SIZE + payloadSize; // we now know the size of the message - we can now know if full message is contained in buffer if ( msgSize <= remSize ) @@ -721,31 +787,216 @@ // TODO - check CRC before processing a message if ( message.crc == crc8( (U08*)(&message), sizeof(MESSAGE_HEADER_T) + message.msg.hdr.payloadLen ) ) { - processReceivedMessage( &message.msg ); +#ifndef ACK_NOT_IMPLEMENTED + // if ACK, mark pending message ACK'd + if ( MSG_ID_ACK == message.msg.hdr.msgID ) + { + matchACKtoPendingACKList( message.msg.hdr.seqNo ); + } + else +#endif + { +#ifndef ACK_NOT_IMPLEMENTED + // if received message requires ACK, queue one up + if ( message.msg.hdr.seqNo < 0 ) + { + sendACKMsg( &message.msg ); + } + else +#endif + { // otherwise, process the received message + processReceivedMessage( &message.msg ); + } + } } else // CRC failed { - // TODO - probably wouldn't want to fault on this. ignore? + checkTooManyBadMsgCRCs( message.msg.hdr.msgID ); } } } +} - // TODO - process UART (script) messages too +/************************************************************************* + * @brief checkForCommTimeouts + * The checkForCommTimeouts function checks for sub-system communication \n + * timeout errors. + * @details + * Inputs : timeOfLastDGCheckIn, timeOfLastUICheckIn + * Outputs : possibly a comm t/o alarm + * @param none + * @return none + *************************************************************************/ +static void checkForCommTimeouts( void ) +{ + if ( TRUE == uiDidCommunicate ) + { + if ( TRUE == didTimeout( timeOfLastUICheckIn, UI_COMM_TIMEOUT_IN_MS ) ) + { + activateAlarmNoData( ALARM_ID_UI_COMM_TIMEOUT ); + } + } + // TODO - check DG comm timeout } /************************************************************************* + * @brief checkTooManyBadMsgCRCs + * The checkTooManyBadMsgCRCs function checks for too many bad message CRCs \n + * within a set period of time. Assumed function is being called when a new \n + * bad CRC is detected so a new bad CRC will be added to the list. + * @details + * Inputs : badCRCTimeStamps[], badCRCListIdx, badCRCListCount + * Outputs : possibly a "too many bad CRCs" alarm + * @param msgID : the message ID from the recevied msg that failed CRC check. + * @return none + *************************************************************************/ +static void checkTooManyBadMsgCRCs( U16 msgID ) +{ + U32 listTimeInMS; + + // replace oldest bad CRC in list with this new one + badCRCTimeStamps[ badCRCListIdx ] = getMSTimerCount(); + // move list index to next position (may wrap) + badCRCListIdx = INC_WRAP( badCRCListIdx, 0, MAX_COMM_CRC_FAILURES - 1 ); + // update list count + badCRCListCount = INC_CAP( badCRCListCount, MAX_COMM_CRC_FAILURES ); + // check if too many bad CRCs in window of time + listTimeInMS = calcTimeSince( badCRCTimeStamps[ badCRCListIdx ] ); + if ( listTimeInMS <= MAX_COMM_CRC_FAILURE_WINDOW_MS ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_COMM_TOO_MANY_BAD_CRCS, (U32)msgID ); + } +} + +#ifndef ACK_NOT_IMPLEMENTED +/************************************************************************* + * @brief addMsgToPendingACKList + * The addMsgToPendingACKList function adds a given message to the pending \n + * ACK list. Messages in this list will require receipt of an ACK message \n + * for this particular message within a limited time. + * @details + * Inputs : pendingAckList[] + * Outputs : pendingAckList[] + * @param msg : pointer to msg within the message data + * @param msgData : pointer to message data to add to pending ACK list + * @param len : # of bytes of message data + * @return TRUE if message added successfully, FALSE if not + *************************************************************************/ +BOOL addMsgToPendingACKList( MESSAGE_T *msg, COMM_BUFFER_T channel, U08 *msgData, U32 len ) +{ + BOOL result = FALSE; + U32 i; + + // find first open slot in pending ACK list and add given msg data to it + for ( i = 0; i < PENDING_ACK_LIST_SIZE; i++ ) + { + _disable_IRQ(); // slot selection needs interrupt protection + if ( FALSE == pendingAckList[ i ].used ) + { + S16 seqNo = msg->hdr.seqNo * -1; // remove ACK bit from seq # + + pendingAckList[ i ].used = TRUE; + _enable_IRQ(); + pendingAckList[ i ].seqNo = seqNo; + pendingAckList[ i ].channel = channel; + pendingAckList[ i ].timeStamp = getMSTimerCount(); + pendingAckList[ i ].retries = MSG_NOT_ACKED_MAX_RETRIES; + pendingAckList[ i ].msgSize = len; + memcpy( pendingAckList[ i ].msg, msgData, len ); + result = TRUE; + break; + } + else + { + _enable_IRQ(); + } + } + + return result; +} + +/************************************************************************* + * @brief matchACKtoPendingACKList + * The matchACKtoPendingACKList function searches the pending ACK list to \n + * see if the sequence # from a received ACK msg matches any. If found, \n + * the list entry is removed. + * @details + * Inputs : pendingAckList[] + * Outputs : pendingAckList[] + * @param seqNo : sequence # to match to an entry in the list + * @return TRUE if a match was found, FALSE if not + *************************************************************************/ +static BOOL matchACKtoPendingACKList( S16 seqNo ) +{ + BOOL result = FALSE; + U32 i; + + // find match + for ( i = 0; i < PENDING_ACK_LIST_SIZE; i++ ) + { + if ( ( TRUE == pendingAckList[ i ].used ) && ( pendingAckList[ i ].seqNo == seqNo ) ) + { // remove message pending ACK from list + pendingAckList[ i ].used = FALSE; + result = TRUE; + break; + } + } + + return result; +} + +/************************************************************************* + * @brief checkPendingACKList + * The checkPendingACKList function searches the pending ACK list to \n + * see if any have expired. Any such messages will be queued for retransmission \n + * and if max retries reached a fault is triggered. + * @details + * Inputs : pendingAckList[] + * Outputs : pendingAckList[] + * @param none + * @return none + *************************************************************************/ +static void checkPendingACKList( void ) +{ + U32 i; + + // find expired messages pending ACK + for ( i = 0; i < PENDING_ACK_LIST_SIZE; i++ ) + { + if ( ( TRUE == pendingAckList[ i ].used ) && ( TRUE == didTimeout( pendingAckList[ i ].timeStamp, MSG_NOT_ACKED_TIMEOUT_MS ) ) ) + { + if ( pendingAckList[ i ].retries > 0 ) + { // re-queue message for transmit + pendingAckList[ i ].retries--; + pendingAckList[ i ].timeStamp = getMSTimerCount(); + addToCommBuffer( pendingAckList[ i ].channel, pendingAckList[ i ].msg, pendingAckList[ i ].msgSize ); + } + else + { + U16 msgID; + + memcpy( &msgID, &pendingAckList[ i ].msg[ sizeof( U08 ) + sizeof( U16) ], sizeof( U16 ) ); + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_CAN_MESSAGE_NOT_ACKED, (U32)msgID ); + } + } + } +} +#endif + +/************************************************************************* * @brief processReceivedMessage * The processReceivedMessage function processes a given message. * @details * Inputs : none * Outputs : message processed - * @param msg : + * @param message : pointer to message to process * @return none *************************************************************************/ static void processReceivedMessage( MESSAGE_T *message ) { U16 msgID = message->hdr.msgID; + // handle any messages from other sub-systems switch ( msgID ) { case MSG_ID_OFF_BUTTON_PRESS: @@ -839,34 +1090,58 @@ handleTestBloodPumpRotorMeasuredSpeedOverrideRequest( message ); break; - case MSG_ID_DIAL_FLOW_SET_PT_OVERRIDE: - handleTestDialFlowSetPointOverrideRequest( message ); + case MSG_ID_DIAL_IN_FLOW_SET_PT_OVERRIDE: + handleTestDialInFlowSetPointOverrideRequest( message ); break; - case MSG_ID_DIAL_FLOW_MEAS_OVERRIDE: - handleTestDialFlowMeasuredOverrideRequest( message ); + case MSG_ID_DIAL_IN_FLOW_MEAS_OVERRIDE: + handleTestDialInFlowMeasuredOverrideRequest( message ); break; - case MSG_ID_DIAL_PUMP_MC_MEAS_SPEED_OVERRIDE: - handleTestDialPumpMCMeasuredSpeedOverrideRequest( message ); + case MSG_ID_DIAL_IN_PUMP_MC_MEAS_SPEED_OVERRIDE: + handleTestDialInPumpMCMeasuredSpeedOverrideRequest( message ); break; - case MSG_ID_DIAL_PUMP_MC_MEAS_CURR_OVERRIDE: - handleTestDialPumpMCMeasuredCurrentOverrideRequest( message ); + case MSG_ID_DIAL_IN_PUMP_MC_MEAS_CURR_OVERRIDE: + handleTestDialInPumpMCMeasuredCurrentOverrideRequest( message ); break; - case MSG_ID_DIAL_FLOW_SEND_INTERVAL_OVERRIDE: - handleTestDialFlowBroadcastIntervalOverrideRequest( message ); + case MSG_ID_DIAL_IN_FLOW_SEND_INTERVAL_OVERRIDE: + handleTestDialInFlowBroadcastIntervalOverrideRequest( message ); break; - case MSG_ID_DIAL_PUMP_MEAS_SPEED_OVERRIDE: - handleTestDialPumpMeasuredSpeedOverrideRequest( message ); + case MSG_ID_DIAL_IN_PUMP_MEAS_SPEED_OVERRIDE: + handleTestDialInPumpMeasuredSpeedOverrideRequest( message ); break; - case MSG_ID_DIAL_PUMP_MEAS_ROTOR_SPEED_OVERRIDE: - handleTestDialPumpRotorMeasuredSpeedOverrideRequest( message ); + case MSG_ID_DIAL_IN_PUMP_MEAS_ROTOR_SPEED_OVERRIDE: + handleTestDialInPumpRotorMeasuredSpeedOverrideRequest( message ); break; + case MSG_ID_PRESSURE_ARTERIAL_OVERRIDE: + handleTestArterialPressureOverrideRequest( message ); + break; + + case MSG_ID_PRESSURE_VENOUS_OVERRIDE: + handleTestVenousPressureOverrideRequest( message ); + break; + + case MSG_ID_OCCLUSION_BLOOD_PUMP_OVERRIDE: + handleTestBloodPumpOcclusionOverrideRequest( message ); + break; + + case MSG_ID_OCCLUSION_DIAL_IN_PUMP_OVERRIDE: + handleTestDialysateInletPumpOcclusionOverrideRequest( message ); + break; + + case MSG_ID_OCCLUSION_DIAL_OUT_PUMP_OVERRIDE: + handleTestDialysateOutletPumpOcclusionOverrideRequest( message ); + break; + + case MSG_ID_PRES_OCCL_SEND_INTERVAL_OVERRIDE: + handleTestPresOcclBroadcastIntervalOverrideRequest( message ); + break; + default: // TODO - unrecognized message ID received - ignore break;