From 4095a7f3350b1b80a06d31989039f693b5bce4d5 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Fri, 1 Aug 2025 09:58:45 -0500 Subject: [PATCH] Feature/mstp valid frame not for us stats (#1053) * Fixed ISO C90 forbids mixed declarations and code warning. * Fixed the MS/TP invalid frame counter that was incremented for valid frames not for us. --- apps/mstpcap/main.c | 4 +- ports/bsd/dlmstp.c | 31 +++++++++++++ ports/bsd/dlmstp_port.c | 6 ++- ports/linux/dlmstp.c | 28 +++++++++++- ports/linux/dlmstp_port.c | 6 ++- ports/linux/mstpsnap.c | 5 +++ ports/linux/rs485.c | 6 +-- ports/win32/dlmstp-mm.c | 2 + ports/win32/dlmstp.c | 21 +++++++++ src/bacnet/datalink/dlenv.c | 3 +- src/bacnet/datalink/dlmstp.c | 34 +++++++++++++- src/bacnet/datalink/dlmstp.h | 5 +++ src/bacnet/datalink/mstp.c | 62 ++++++++++++++++++++------ src/bacnet/datalink/mstp.h | 7 +++ test/bacnet/datalink/dlmstp/src/main.c | 3 ++ 15 files changed, 198 insertions(+), 25 deletions(-) diff --git a/apps/mstpcap/main.c b/apps/mstpcap/main.c index 821b0c56..d119f2a4 100644 --- a/apps/mstpcap/main.c +++ b/apps/mstpcap/main.c @@ -984,6 +984,7 @@ static void mstp_structure_init(struct mstp_port_struct_t *mstp_port) mstp_port->DataRegister = 0xFF; mstp_port->ReceivedInvalidFrame = false; mstp_port->ReceivedValidFrame = false; + mstp_port->ReceivedValidFrameNotForUs = false; mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; mstp_port->SilenceTimerReset(NULL); } @@ -1163,7 +1164,8 @@ int main(int argc, char *argv[]) RS485_Check_UART_Data(mstp_port); MSTP_Receive_Frame_FSM(mstp_port); /* process the data portion of the frame */ - if (mstp_port->ReceivedValidFrame) { + if (mstp_port->ReceivedValidFrame || + mstp_port->ReceivedValidFrameNotForUs) { write_received_packet(mstp_port, MSTP_HEADER_MAX); mstp_structure_init(mstp_port); packet_count++; diff --git a/ports/bsd/dlmstp.c b/ports/bsd/dlmstp.c index b5015244..1480da93 100644 --- a/ports/bsd/dlmstp.c +++ b/ports/bsd/dlmstp.c @@ -67,6 +67,7 @@ static struct mstimer Valid_Frame_Timer; /* callbacks for monitoring */ static dlmstp_hook_frame_rx_start_cb Preamble_Callback; static dlmstp_hook_frame_rx_complete_cb Valid_Frame_Rx_Callback; +static dlmstp_hook_frame_rx_complete_cb Valid_Frame_Not_For_Us_Rx_Callback; static dlmstp_hook_frame_rx_complete_cb Invalid_Frame_Rx_Callback; static DLMSTP_STATISTICS DLMSTP_Statistics; @@ -498,6 +499,7 @@ static void *dlmstp_thread(void *pArg) while (thread_alive) { /* only do receive state machine while we don't have a frame */ if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedValidFrameNotForUs == false) && (MSTP_Port.ReceivedInvalidFrame == false)) { RS485_Check_UART_Data(&MSTP_Port); MSTP_Receive_Frame_FSM(&MSTP_Port); @@ -516,6 +518,15 @@ static void *dlmstp_thread(void *pArg) MSTP_Port.DataLength); } run_master = true; + } else if (MSTP_Port.ReceivedValidFrameNotForUs) { + DLMSTP_Statistics.receive_valid_frame_not_for_us_counter++; + if (Valid_Frame_Not_For_Us_Rx_Callback) { + Valid_Frame_Not_For_Us_Rx_Callback( + MSTP_Port.SourceAddress, MSTP_Port.DestinationAddress, + MSTP_Port.FrameType, MSTP_Port.InputBuffer, + MSTP_Port.DataLength); + } + run_master = true; } else if (MSTP_Port.ReceivedInvalidFrame) { if (Invalid_Frame_Rx_Callback) { DLMSTP_Statistics.receive_invalid_frame_counter++; @@ -872,6 +883,26 @@ void dlmstp_set_frame_rx_complete_callback( Valid_Frame_Rx_Callback = cb_func; } +/** + * @brief Set the MS/TP Frame Complete callback + * @param cb_func - callback function to be called when a frame is received + */ +void dlmstp_set_frame_not_for_us_rx_complete_callback( + dlmstp_hook_frame_rx_complete_cb cb_func) +{ + Valid_Frame_Not_For_Us_Rx_Callback = cb_func; +} + +/** + * @brief Set the MS/TP Frame Complete callback + * @param cb_func - callback function to be called when a frame is received + */ +void dlmstp_set_invalid_frame_rx_complete_callback( + dlmstp_hook_frame_rx_complete_cb cb_func) +{ + Invalid_Frame_Rx_Callback = cb_func; +} + /** * @brief Set the MS/TP Frame Complete callback * @param cb_func - callback function to be called when a frame is received diff --git a/ports/bsd/dlmstp_port.c b/ports/bsd/dlmstp_port.c index de3c76e0..917552f8 100644 --- a/ports/bsd/dlmstp_port.c +++ b/ports/bsd/dlmstp_port.c @@ -266,11 +266,13 @@ static void *dlmstp_receive_fsm_task(void *pArg) for (;;) { /* only do receive state machine while we don't have a frame */ if ((mstp_port->ReceivedValidFrame == false) && + (mstp_port->ReceivedValidFrameNotForUs == false) && (mstp_port->ReceivedInvalidFrame == false)) { do { RS485_Check_UART_Data(mstp_port); MSTP_Receive_Frame_FSM((struct mstp_port_struct_t *)pArg); received_frame = mstp_port->ReceivedValidFrame || + mstp_port->ReceivedValidFrameNotForUs || mstp_port->ReceivedInvalidFrame; if (received_frame) { pthread_cond_signal(&poSharedData->Received_Frame_Flag); @@ -301,11 +303,13 @@ static void *dlmstp_master_fsm_task(void *pArg) for (;;) { if (mstp_port->ReceivedValidFrame == false && + mstp_port->ReceivedValidFrameNotForUs == false && mstp_port->ReceivedInvalidFrame == false) { RS485_Check_UART_Data(mstp_port); MSTP_Receive_Frame_FSM(mstp_port); } - if (mstp_port->ReceivedValidFrame || mstp_port->ReceivedInvalidFrame) { + if (mstp_port->ReceivedValidFrame || mstp_port->ReceivedInvalidFrame || + mstp_port->ReceivedValidFrameNotForUs) { run_master = true; } else { silence = mstp_port->SilenceTimer(NULL); diff --git a/ports/linux/dlmstp.c b/ports/linux/dlmstp.c index 73f72827..38c75538 100644 --- a/ports/linux/dlmstp.c +++ b/ports/linux/dlmstp.c @@ -71,6 +71,7 @@ static struct mstimer Valid_Frame_Timer; /* callbacks for monitoring */ static dlmstp_hook_frame_rx_start_cb Preamble_Callback; static dlmstp_hook_frame_rx_complete_cb Valid_Frame_Rx_Callback; +static dlmstp_hook_frame_rx_complete_cb Valid_Frame_Not_For_Us_Rx_Callback; static dlmstp_hook_frame_rx_complete_cb Invalid_Frame_Rx_Callback; static DLMSTP_STATISTICS DLMSTP_Statistics; @@ -429,9 +430,10 @@ void MSTP_Send_Frame( uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port) { uint16_t pdu_len = 0; + DLMSTP_PACKET *pkt; pthread_mutex_lock(&Receive_Packet_Mutex); - DLMSTP_PACKET *pkt = (DLMSTP_PACKET *)Ringbuf_Data_Peek(&Receive_Queue); + pkt = (DLMSTP_PACKET *)Ringbuf_Data_Peek(&Receive_Queue); if (!pkt) { debug_printf("MS/TP: Dropped! Not Ready.\n"); } else { @@ -519,6 +521,7 @@ static void *dlmstp_thread(void *pArg) while (thread_alive) { /* only do receive state machine while we don't have a frame */ if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedValidFrameNotForUs == false) && (MSTP_Port.ReceivedInvalidFrame == false)) { RS485_Check_UART_Data(&MSTP_Port); MSTP_Receive_Frame_FSM(&MSTP_Port); @@ -537,6 +540,17 @@ static void *dlmstp_thread(void *pArg) MSTP_Port.DataLength); } run_master = true; + } else if (MSTP_Port.ReceivedValidFrameNotForUs) { + DLMSTP_Statistics.receive_valid_frame_not_for_us_counter++; + if (Valid_Frame_Not_For_Us_Rx_Callback) { + Valid_Frame_Not_For_Us_Rx_Callback( + MSTP_Port.SourceAddress, MSTP_Port.DestinationAddress, + MSTP_Port.FrameType, MSTP_Port.InputBuffer, + MSTP_Port.DataLength); + } + run_master = true; + /* we don't run the master state machine for this frame */ + MSTP_Port.ReceivedValidFrameNotForUs = false; } else if (MSTP_Port.ReceivedInvalidFrame) { if (Invalid_Frame_Rx_Callback) { DLMSTP_Statistics.receive_invalid_frame_counter++; @@ -893,6 +907,16 @@ void dlmstp_set_frame_rx_complete_callback( Valid_Frame_Rx_Callback = cb_func; } +/** + * @brief Set the MS/TP Frame Complete callback + * @param cb_func - callback function to be called when a frame is received + */ +void dlmstp_set_frame_not_for_us_rx_complete_callback( + dlmstp_hook_frame_rx_complete_cb cb_func) +{ + Valid_Frame_Not_For_Us_Rx_Callback = cb_func; +} + /** * @brief Set the MS/TP Frame Complete callback * @param cb_func - callback function to be called when a frame is received @@ -1091,7 +1115,7 @@ bool dlmstp_init(char *ifname) #endif pthread_attr_init(&thread_attr); - // Set scheduling policy to SCHED_FIFO and priority + /* Set scheduling policy to SCHED_FIFO and priority */ rv = pthread_attr_setinheritsched(&thread_attr, PTHREAD_EXPLICIT_SCHED); if (rv != 0) { fprintf( diff --git a/ports/linux/dlmstp_port.c b/ports/linux/dlmstp_port.c index 8654bb95..341845e3 100644 --- a/ports/linux/dlmstp_port.c +++ b/ports/linux/dlmstp_port.c @@ -210,11 +210,13 @@ static void *dlmstp_receive_fsm_task(void *pArg) for (;;) { /* only do receive state machine while we don't have a frame */ if ((mstp_port->ReceivedValidFrame == false) && + (mstp_port->ReceivedValidFrameNotForUs == false) && (mstp_port->ReceivedInvalidFrame == false)) { do { RS485_Check_UART_Data(mstp_port); MSTP_Receive_Frame_FSM((struct mstp_port_struct_t *)pArg); received_frame = mstp_port->ReceivedValidFrame || + mstp_port->ReceivedValidFrameNotForUs || mstp_port->ReceivedInvalidFrame; if (received_frame) { pthread_cond_signal(&poSharedData->Received_Frame_Flag); @@ -245,11 +247,13 @@ static void *dlmstp_master_fsm_task(void *pArg) for (;;) { if (mstp_port->ReceivedValidFrame == false && + mstp_port->ReceivedValidFrameNotForUs == false && mstp_port->ReceivedInvalidFrame == false) { RS485_Check_UART_Data(mstp_port); MSTP_Receive_Frame_FSM(mstp_port); } - if (mstp_port->ReceivedValidFrame || mstp_port->ReceivedInvalidFrame) { + if (mstp_port->ReceivedValidFrame || mstp_port->ReceivedInvalidFrame || + mstp_port->ReceivedValidFrameNotForUs) { run_master = true; } else { silence = mstp_port->SilenceTimer(NULL); diff --git a/ports/linux/mstpsnap.c b/ports/linux/mstpsnap.c index cf06609b..16e8f494 100644 --- a/ports/linux/mstpsnap.c +++ b/ports/linux/mstpsnap.c @@ -283,6 +283,11 @@ int main(int argc, char *argv[]) mstp_port->ReceivedValidFrame = false; snap_received_packet(mstp_port, sockfd); packet_count++; + } else if (mstp_port->ReceivedValidFrameNotForUs) { + mstp_port->ReceivedValidFrameNotForUs = false; + fprintf(stderr, "ReceivedValidFrameNotForUs\n"); + snap_received_packet(mstp_port, sockfd); + packet_count++; } else if (mstp_port->ReceivedInvalidFrame) { mstp_port->ReceivedInvalidFrame = false; fprintf(stderr, "ReceivedInvalidFrame\n"); diff --git a/ports/linux/rs485.c b/ports/linux/rs485.c index dacf6562..d16c9e87 100644 --- a/ports/linux/rs485.c +++ b/ports/linux/rs485.c @@ -248,16 +248,16 @@ void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port) uint8_t buf[2048]; ssize_t n; int handle = RS485_Handle; + SHARED_MSTP_DATA *poSharedData; FIFO_BUFFER *fifo = &Rx_FIFO; + waiter.tv_sec = 0; waiter.tv_usec = 5000; - - SHARED_MSTP_DATA *poSharedData = (SHARED_MSTP_DATA *)mstp_port->UserData; + poSharedData = (SHARED_MSTP_DATA *)mstp_port->UserData; if (poSharedData) { handle = poSharedData->RS485_Handle; fifo = &poSharedData->Rx_FIFO; } - if (mstp_port->ReceiveError == true) { /* do nothing but wait for state machine to clear the error */ } else if (mstp_port->DataAvailable == false) { diff --git a/ports/win32/dlmstp-mm.c b/ports/win32/dlmstp-mm.c index 3c816800..f047f811 100644 --- a/ports/win32/dlmstp-mm.c +++ b/ports/win32/dlmstp-mm.c @@ -165,11 +165,13 @@ static void dlmstp_receive_fsm_task(void *pArg) while (TRUE) { /* only do receive state machine while we don't have a frame */ if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedValidFrameNotForUs == false) && (MSTP_Port.ReceivedInvalidFrame == false)) { do { RS485_Check_UART_Data(&MSTP_Port); MSTP_Receive_Frame_FSM(&MSTP_Port); received_frame = MSTP_Port.ReceivedValidFrame || + MSTP_Port.ReceivedValidFrameNotForUs || MSTP_Port.ReceivedInvalidFrame; if (received_frame) { ReleaseSemaphore(Received_Frame_Flag, 1, NULL); diff --git a/ports/win32/dlmstp.c b/ports/win32/dlmstp.c index 5e25293f..6cafe2ef 100644 --- a/ports/win32/dlmstp.c +++ b/ports/win32/dlmstp.c @@ -60,6 +60,7 @@ static struct mstimer Valid_Frame_Timer; /* callbacks for monitoring */ static dlmstp_hook_frame_rx_start_cb Preamble_Callback; static dlmstp_hook_frame_rx_complete_cb Valid_Frame_Rx_Callback; +static dlmstp_hook_frame_rx_complete_cb Valid_Frame_Not_For_Us_Rx_Callback; static dlmstp_hook_frame_rx_complete_cb Invalid_Frame_Rx_Callback; static DLMSTP_STATISTICS DLMSTP_Statistics; @@ -430,6 +431,7 @@ static void dlmstp_thread(void *pArg) for (;;) { /* only do receive state machine while we don't have a frame */ if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedValidFrameNotForUs == false) && (MSTP_Port.ReceivedInvalidFrame == false)) { /* note: RS485 waits up to 1ms for data to arrive */ RS485_Check_UART_Data(&MSTP_Port); @@ -449,6 +451,15 @@ static void dlmstp_thread(void *pArg) MSTP_Port.DataLength); } run_master = true; + } else if (MSTP_Port.ReceivedValidFrameNotForUs) { + DLMSTP_Statistics.receive_valid_frame_not_for_us_counter++; + if (Valid_Frame_Not_For_Us_Rx_Callback) { + Valid_Frame_Not_For_Us_Rx_Callback( + MSTP_Port.SourceAddress, MSTP_Port.DestinationAddress, + MSTP_Port.FrameType, MSTP_Port.InputBuffer, + MSTP_Port.DataLength); + } + run_master = true; } else if (MSTP_Port.ReceivedInvalidFrame) { if (Invalid_Frame_Rx_Callback) { DLMSTP_Statistics.receive_invalid_frame_counter++; @@ -793,6 +804,16 @@ void dlmstp_set_frame_rx_complete_callback( Valid_Frame_Rx_Callback = cb_func; } +/** + * @brief Set the MS/TP Frame Not For Us callback + * @param cb_func - callback function to be called when a frame is received + */ +void dlmstp_set_frame_not_for_us_rx_complete_callback( + dlmstp_hook_frame_rx_complete_cb cb_func) +{ + Valid_Frame_Not_For_Us_Rx_Callback = cb_func; +} + /** * @brief Set the MS/TP Frame Complete callback * @param cb_func - callback function to be called when a frame is received diff --git a/src/bacnet/datalink/dlenv.c b/src/bacnet/datalink/dlenv.c index 01c49a19..bd6d1e5b 100644 --- a/src/bacnet/datalink/dlenv.c +++ b/src/bacnet/datalink/dlenv.c @@ -831,8 +831,9 @@ void dlenv_maintenance_timer(uint16_t elapsed_seconds) dlmstp_fill_statistics(&statistics); fprintf( stderr, - "MSTP: Frames Rx:%u/%u Tx:%u PDU Rx:%u Tx:%u Lost:%u\n", + "MSTP: Frames Rx:%u/%u/%u Tx:%u PDU Rx:%u Tx:%u Lost:%u\n", statistics.receive_valid_frame_counter, + statistics.receive_valid_frame_not_for_us_counter, statistics.receive_invalid_frame_counter, statistics.transmit_frame_counter, statistics.receive_pdu_counter, diff --git a/src/bacnet/datalink/dlmstp.c b/src/bacnet/datalink/dlmstp.c index 1b9c9bfc..39f1e0aa 100644 --- a/src/bacnet/datalink/dlmstp.c +++ b/src/bacnet/datalink/dlmstp.c @@ -372,7 +372,8 @@ uint16_t dlmstp_receive( } /* only do receive state machine while we don't have a frame */ while ((MSTP_Port->ReceivedValidFrame == false) && - (MSTP_Port->ReceivedInvalidFrame == false)) { + (MSTP_Port->ReceivedInvalidFrame == false) && + (MSTP_Port->ReceivedValidFrameNotForUs == false)) { MSTP_Port->DataAvailable = driver->read(&data_register); if (MSTP_Port->DataAvailable) { MSTP_Port->DataRegister = data_register; @@ -388,7 +389,8 @@ uint16_t dlmstp_receive( break; } } - if (MSTP_Port->ReceivedValidFrame || MSTP_Port->ReceivedInvalidFrame) { + if (MSTP_Port->ReceivedValidFrame || MSTP_Port->ReceivedInvalidFrame || + MSTP_Port->ReceivedValidFrameNotForUs) { /* delay after reception before transmitting - per MS/TP spec */ milliseconds = MSTP_Port->SilenceTimer(MSTP_Port); if (milliseconds < MSTP_Port->Tturnaround_timeout) { @@ -405,6 +407,15 @@ uint16_t dlmstp_receive( MSTP_Port->DataLength); } } + if (MSTP_Port->ReceivedValidFrameNotForUs) { + user->Statistics.receive_valid_frame_not_for_us_counter++; + if (user->Valid_Frame_Not_For_Us_Rx_Callback) { + user->Valid_Frame_Not_For_Us_Rx_Callback( + MSTP_Port->SourceAddress, MSTP_Port->DestinationAddress, + MSTP_Port->FrameType, MSTP_Port->InputBuffer, + MSTP_Port->DataLength); + } + } if (MSTP_Port->ReceivedInvalidFrame) { user->Statistics.receive_invalid_frame_counter++; if (user->Invalid_Frame_Rx_Callback) { @@ -885,6 +896,25 @@ void dlmstp_set_frame_rx_complete_callback( user->Valid_Frame_Rx_Callback = cb_func; } +/** + * @brief Set the MS/TP Frame Complete callback + * @param cb_func - callback function to be called when a frame is received + */ +void dlmstp_set_frame_not_for_us_rx_complete_callback( + dlmstp_hook_frame_rx_complete_cb cb_func) +{ + struct dlmstp_user_data_t *user; + + if (!MSTP_Port) { + return; + } + user = MSTP_Port->UserData; + if (!user) { + return; + } + user->Valid_Frame_Not_For_Us_Rx_Callback = cb_func; +} + /** * @brief Set the MS/TP Frame Complete callback * @param cb_func - callback function to be called when a frame is received diff --git a/src/bacnet/datalink/dlmstp.h b/src/bacnet/datalink/dlmstp.h index 9459b8d2..235009da 100644 --- a/src/bacnet/datalink/dlmstp.h +++ b/src/bacnet/datalink/dlmstp.h @@ -38,6 +38,7 @@ typedef struct dlmstp_statistics { uint32_t transmit_frame_counter; uint32_t receive_valid_frame_counter; uint32_t receive_invalid_frame_counter; + uint32_t receive_valid_frame_not_for_us_counter; uint32_t transmit_pdu_counter; uint32_t receive_pdu_counter; uint32_t lost_token_counter; @@ -101,6 +102,7 @@ struct dlmstp_user_data_t { struct dlmstp_rs485_driver *RS485_Driver; dlmstp_hook_frame_rx_start_cb Preamble_Callback; dlmstp_hook_frame_rx_complete_cb Valid_Frame_Rx_Callback; + dlmstp_hook_frame_rx_complete_cb Valid_Frame_Not_For_Us_Rx_Callback; dlmstp_hook_frame_rx_complete_cb Invalid_Frame_Rx_Callback; uint32_t Valid_Frame_Milliseconds; /* the PDU Queue is made of Nmax_info_frames x dlmstp_packet's */ @@ -230,6 +232,9 @@ BACNET_STACK_EXPORT void dlmstp_set_frame_rx_complete_callback( dlmstp_hook_frame_rx_complete_cb cb_func); BACNET_STACK_EXPORT +void dlmstp_set_frame_not_for_us_rx_complete_callback( + dlmstp_hook_frame_rx_complete_cb cb_func); +BACNET_STACK_EXPORT void dlmstp_set_invalid_frame_rx_complete_callback( dlmstp_hook_frame_rx_complete_cb cb_func); diff --git a/src/bacnet/datalink/mstp.c b/src/bacnet/datalink/mstp.c index df834bf4..cd2c43ae 100644 --- a/src/bacnet/datalink/mstp.c +++ b/src/bacnet/datalink/mstp.c @@ -450,7 +450,7 @@ void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *mstp_port) mstp_port->ReceivedValidFrame = true; } else { /* NotForUs */ - mstp_port->ReceivedInvalidFrame = true; + mstp_port->ReceivedValidFrameNotForUs = true; } /* wait for the start of the next frame. */ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; @@ -563,25 +563,34 @@ void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *mstp_port) &mstp_port->InputBuffer[mstp_port->Index + 1], mstp_port->InputBufferSize, mstp_port->InputBuffer, mstp_port->Index + 1); - if ((mstp_port->DataLength > 0) && - (mstp_port->receive_state == - MSTP_RECEIVE_STATE_DATA)) { + if (mstp_port->DataLength > 0) { /* GoodCRC */ - mstp_port->ReceivedValidFrame = true; + if (mstp_port->receive_state == + MSTP_RECEIVE_STATE_DATA) { + /* ForUs */ + mstp_port->ReceivedValidFrame = true; + } else { + /* NotForUs */ + mstp_port->ReceivedValidFrameNotForUs = true; + } } else { - /* Done */ + /* BadCRC */ mstp_port->ReceivedInvalidFrame = true; + printf_receive_error( + "MSTP: Rx Data: BadCRC [%02X]\n", + mstp_port->DataRegister); } } else { /* STATE DATA CRC - no need for new state */ if (mstp_port->DataCRC == 0xF0B8) { + /* GoodCRC */ if (mstp_port->receive_state == MSTP_RECEIVE_STATE_DATA) { - /* GoodCRC */ + /* ForUs */ mstp_port->ReceivedValidFrame = true; } else { - /* Done */ - mstp_port->ReceivedInvalidFrame = true; + /* NotForUs */ + mstp_port->ReceivedValidFrameNotForUs = true; } } else { /* BadCRC */ @@ -691,6 +700,11 @@ bool MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port) /* invalid frame was received */ /* wait for the next frame - remain in IDLE */ mstp_port->ReceivedInvalidFrame = false; + } else if (mstp_port->ReceivedValidFrameNotForUs == true) { + /* ReceivedValidFrameNotForUs */ + /* valid frame was received, but not for this node */ + /* wait for the next frame - remain in IDLE */ + mstp_port->ReceivedValidFrameNotForUs = false; } else if (mstp_port->ReceivedValidFrame == true) { printf_master( "MSTP: ReceivedValidFrame " @@ -783,6 +797,7 @@ bool MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port) /* set the receive frame flags to false in case we received some bytes and had a timeout for some reason */ mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrameNotForUs = false; mstp_port->ReceivedValidFrame = false; transition_now = true; } @@ -848,10 +863,11 @@ bool MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port) /* of the initial value of FrameCount.) */ transition_now = true; } else { - if (mstp_port->ReceivedInvalidFrame == true) { - /* InvalidFrame */ - /* error in frame reception */ + if ((mstp_port->ReceivedInvalidFrame == true) || + (mstp_port->ReceivedValidFrameNotForUs == true)) { + /* InvalidFrame in this state */ mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrameNotForUs = false; mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; transition_now = true; } else if (mstp_port->ReceivedValidFrame == true) { @@ -1123,7 +1139,8 @@ bool MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port) } else if ( (mstp_port->SilenceTimer((void *)mstp_port) > mstp_port->Tusage_timeout) || - (mstp_port->ReceivedInvalidFrame == true)) { + (mstp_port->ReceivedInvalidFrame == true) || + (mstp_port->ReceivedValidFrameNotForUs == true)) { if (mstp_port->SoleMaster == true) { /* SoleMaster */ /* There was no valid reply to the periodic poll */ @@ -1169,6 +1186,7 @@ bool MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port) } } mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrameNotForUs = false; } break; case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: @@ -1246,6 +1264,10 @@ void MSTP_Slave_Node_FSM(struct mstp_port_struct_t *mstp_port) /* ReceivedInvalidFrame */ /* invalid frame was received */ mstp_port->ReceivedInvalidFrame = false; + } else if (mstp_port->ReceivedValidFrameNotForUs) { + /* ReceivedValidFrameNotForUs */ + /* valid frame was received, but not for this node */ + mstp_port->ReceivedValidFrameNotForUs = false; } else if (mstp_port->ReceivedValidFrame) { mstp_port->ReceivedValidFrame = false; switch (mstp_port->FrameType) { @@ -1438,8 +1460,11 @@ static void MSTP_Zero_Config_State_Idle(struct mstp_port_struct_t *mstp_port) mstp_port->Poll_Count = 0; mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK; } else if (mstp_port->ReceivedInvalidFrame) { - /* InvalidFrame */ + /* IdleInvalidFrame */ mstp_port->ReceivedInvalidFrame = false; + } else if (mstp_port->ReceivedValidFrameNotForUs) { + /* IdleValidFrameNotForUs */ + mstp_port->ReceivedValidFrameNotForUs = false; } else if (mstp_port->Zero_Config_Silence > 0) { if (mstp_port->SilenceTimer((void *)mstp_port) > mstp_port->Zero_Config_Silence) { @@ -1509,6 +1534,9 @@ static void MSTP_Zero_Config_State_Lurk(struct mstp_port_struct_t *mstp_port) } else if (mstp_port->ReceivedInvalidFrame) { /* LurkInvalidFrame */ mstp_port->ReceivedInvalidFrame = false; + } else if (mstp_port->ReceivedValidFrameNotForUs) { + /* LurkValidFrameNotForUs */ + mstp_port->ReceivedValidFrameNotForUs = false; } else if (mstp_port->Zero_Config_Silence > 0) { if (mstp_port->SilenceTimer((void *)mstp_port) > mstp_port->Zero_Config_Silence) { @@ -1557,6 +1585,9 @@ static void MSTP_Zero_Config_State_Claim(struct mstp_port_struct_t *mstp_port) } else if (mstp_port->ReceivedInvalidFrame) { /* ClaimInvalidFrame */ mstp_port->ReceivedInvalidFrame = false; + } else if (mstp_port->ReceivedValidFrameNotForUs) { + /* ClaimValidFrameNotForUs */ + mstp_port->ReceivedValidFrameNotForUs = false; } else if (mstp_port->Zero_Config_Silence > 0) { /* ClaimTimeout */ if (mstp_port->SilenceTimer((void *)mstp_port) > @@ -1617,6 +1648,9 @@ static void MSTP_Zero_Config_State_Confirm(struct mstp_port_struct_t *mstp_port) } else if (mstp_port->ReceivedInvalidFrame) { /* ConfirmationInvalidFrame */ mstp_port->ReceivedInvalidFrame = false; + } else if (mstp_port->ReceivedValidFrameNotForUs) { + /* ConfirmationValidFrameNotForUs */ + mstp_port->ReceivedValidFrameNotForUs = false; } else if ( mstp_port->SilenceTimer((void *)mstp_port) >= mstp_port->Treply_timeout) { diff --git a/src/bacnet/datalink/mstp.h b/src/bacnet/datalink/mstp.h index 9aeb812f..a0fb56ef 100644 --- a/src/bacnet/datalink/mstp.h +++ b/src/bacnet/datalink/mstp.h @@ -39,8 +39,15 @@ struct mstp_port_struct_t { unsigned ReceiveError : 1; /* There is data in the buffer */ unsigned DataAvailable : 1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an invalid frame is received. */ + /* Set to FALSE by the main state machine. */ unsigned ReceivedInvalidFrame : 1; /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is not for us */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrameNotForUs : 1; + /* A Boolean flag set to TRUE by the Receive State Machine */ /* if a valid frame is received. */ /* Set to FALSE by the Master or Slave Node state machine. */ unsigned ReceivedValidFrame : 1; diff --git a/test/bacnet/datalink/dlmstp/src/main.c b/test/bacnet/datalink/dlmstp/src/main.c index e98e4100..f60299bd 100644 --- a/test/bacnet/datalink/dlmstp/src/main.c +++ b/test/bacnet/datalink/dlmstp/src/main.c @@ -116,6 +116,9 @@ static void test_MSTP_Datalink(void) zassert_equal( test_stats.receive_valid_frame_counter, MSTP_User.Statistics.receive_valid_frame_counter, NULL); + zassert_equal( + test_stats.receive_valid_frame_not_for_us_counter, + MSTP_User.Statistics.receive_valid_frame_not_for_us_counter, NULL); zassert_equal( test_stats.receive_invalid_frame_counter, MSTP_User.Statistics.receive_invalid_frame_counter, NULL);