From e53f38dd40e3ae42423206c39f7a86a4f72999d1 Mon Sep 17 00:00:00 2001 From: skarg Date: Sat, 14 Jul 2007 00:09:16 +0000 Subject: [PATCH] Changed the MSTP_MASTER_STATE_ANSWER_DATA_REQUEST state to always send a REPLY_POSTPONED by commenting out the section that checks for new packets to send. It was not the intent of the BACnet standard, but it makes the Master Node FSM compliant. It's either that or re-parse the message for a matching apdu and invoke id, and then defer if the next message does not match. The problem happens when an unconfirmed request (i.e. WhoIs) is followed immediated by a confirmed request (i.e. ReadProperty) before the device can respond to the unconfirmed request (i.e. the I-Am is queued in the Transmit buffer). --- bacnet-stack/dlmstp.h | 49 +++++ bacnet-stack/ports/linux/mstp.c | 11 +- bacnet-stack/ports/pic18f6720/mstp.c | 130 ++---------- bacnet-stack/ports/rtos32/mstp.c | 48 ++--- bacnet-stack/ports/win32/dlmstp.c | 80 ++++--- bacnet-stack/ports/win32/dlmstp.cbp | 9 + bacnet-stack/ports/win32/mstp.c | 304 ++++++++++----------------- bacnet-stack/ports/win32/mstp.h | 81 ++----- 8 files changed, 271 insertions(+), 441 deletions(-) diff --git a/bacnet-stack/dlmstp.h b/bacnet-stack/dlmstp.h index a5d3918d..3c9ee66a 100644 --- a/bacnet-stack/dlmstp.h +++ b/bacnet-stack/dlmstp.h @@ -45,6 +45,55 @@ #define MAX_HEADER (2+1+1+1+2+1+2+1) #define MAX_MPDU (MAX_HEADER+MAX_PDU) +/* The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for a station. */ +/* Station addresses for master nodes can be 0-127. */ +/* Station addresses for slave nodes can be 127-254. */ +#define MSTP_BROADCAST_ADDRESS 255 + +/* MS/TP Frame Type */ +/* Frame Types 8 through 127 are reserved by ASHRAE. */ +#define FRAME_TYPE_TOKEN 0 +#define FRAME_TYPE_POLL_FOR_MASTER 1 +#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2 +#define FRAME_TYPE_TEST_REQUEST 3 +#define FRAME_TYPE_TEST_RESPONSE 4 +#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5 +#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6 +#define FRAME_TYPE_REPLY_POSTPONED 7 +/* Frame Types 128 through 255: Proprietary Frames */ +/* These frames are available to vendors as proprietary (non-BACnet) frames. */ +/* The first two octets of the Data field shall specify the unique vendor */ +/* identification code, most significant octet first, for the type of */ +/* vendor-proprietary frame to be conveyed. The length of the data portion */ +/* of a Proprietary frame shall be in the range of 2 to 501 octets. */ +#define FRAME_TYPE_PROPRIETARY_MIN 128 +#define FRAME_TYPE_PROPRIETARY_MAX 255 +/* The initial CRC16 checksum value */ +#define CRC16_INITIAL_VALUE (0xFFFF) + +/* receive FSM states */ +typedef enum { + MSTP_RECEIVE_STATE_IDLE = 0, + MSTP_RECEIVE_STATE_PREAMBLE = 1, + MSTP_RECEIVE_STATE_HEADER = 2, + MSTP_RECEIVE_STATE_HEADER_CRC = 3, + MSTP_RECEIVE_STATE_DATA = 4 +} MSTP_RECEIVE_STATE; + +/* master node FSM states */ +typedef enum { + MSTP_MASTER_STATE_INITIALIZE = 0, + MSTP_MASTER_STATE_IDLE = 1, + MSTP_MASTER_STATE_USE_TOKEN = 2, + MSTP_MASTER_STATE_WAIT_FOR_REPLY = 3, + MSTP_MASTER_STATE_DONE_WITH_TOKEN = 4, + MSTP_MASTER_STATE_PASS_TOKEN = 5, + MSTP_MASTER_STATE_NO_TOKEN = 6, + MSTP_MASTER_STATE_POLL_FOR_MASTER = 7, + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8 +} MSTP_MASTER_STATE; + typedef struct dlmstp_packet { bool ready; /* true if ready to be sent or received */ BACNET_ADDRESS address; /* source address */ diff --git a/bacnet-stack/ports/linux/mstp.c b/bacnet-stack/ports/linux/mstp.c index f5da3012..154e4388 100644 --- a/bacnet-stack/ports/linux/mstp.c +++ b/bacnet-stack/ports/linux/mstp.c @@ -1177,9 +1177,11 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) /* BACnet Data Expecting Reply, a Test_Request, or */ /* a proprietary frame that expects a reply is received. */ case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: +#if 0 + /* FIXME: we always defer the reply to be safe */ /* FIXME: if we knew the APDU type received, we could - see if the next message was that same APDU type */ - /* FIXME: we could always defer the reply to be safe */ + see if the next message was that same APDU type + along with the matching src/dest and invoke ID */ if ((mstp_port->SilenceTimer <= Treply_delay) && mstp_port->TxReady) { /* Reply */ @@ -1203,7 +1205,8 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) mstp_port->TxReady = false; mstp_port->master_state = MSTP_MASTER_STATE_IDLE; } - } + } else +#endif /* DeferredReply */ /* If no reply will be available from the higher layers */ /* within Treply_delay after the reception of the */ @@ -1213,7 +1216,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) /* Any reply shall wait until this node receives the token. */ /* Call MSTP_Create_And_Send_Frame to transmit a Reply Postponed frame, */ /* and enter the IDLE state. */ - else { + { MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_REPLY_POSTPONED, mstp_port->SourceAddress, diff --git a/bacnet-stack/ports/pic18f6720/mstp.c b/bacnet-stack/ports/pic18f6720/mstp.c index 47a3d6bd..faae1286 100644 --- a/bacnet-stack/ports/pic18f6720/mstp.c +++ b/bacnet-stack/ports/pic18f6720/mstp.c @@ -53,6 +53,9 @@ #include "bytes.h" #include "crc.h" #include "rs485.h" +#if PRINT_ENABLED +#include "mstptext.h" +#endif /* debug print statements */ #if PRINT_ENABLED @@ -227,35 +230,6 @@ void MSTP_Create_And_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* FIXME: be sure to reset SilenceTimer after each octet is sent! */ } -#if PRINT_ENABLED_RECEIVE -char *mstp_receive_state_text(int state) -{ - char *text = "unknown"; - - switch (state) { - case MSTP_RECEIVE_STATE_IDLE: - text = "IDLE"; - break; - case MSTP_RECEIVE_STATE_PREAMBLE: - text = "PREAMBLE"; - break; - case MSTP_RECEIVE_STATE_HEADER: - text = "HEADER"; - break; - case MSTP_RECEIVE_STATE_HEADER_CRC: - text = "HEADER_CRC"; - break; - case MSTP_RECEIVE_STATE_DATA: - text = "DATA"; - break; - default: - break; - } - - return text; -} -#endif - void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) { #if PRINT_ENABLED_RECEIVE_DATA @@ -264,7 +238,7 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) #if PRINT_ENABLED_RECEIVE fprintf(stderr, "MSTP Rx: State=%s Data=%02X hCRC=%02X Index=%u EC=%u DateLen=%u Silence=%u\n", - mstp_receive_state_text(mstp_port->receive_state), + mstptext_receive_state(mstp_port->receive_state), mstp_port->DataRegister, mstp_port->HeaderCRC, mstp_port->Index, mstp_port->EventCount, mstp_port->DataLength, mstp_port->SilenceTimer); @@ -576,88 +550,6 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) return; } -#if PRINT_ENABLED -char *mstp_master_state_text(int state) -{ - char *text = "unknown"; - - switch (state) { - case MSTP_MASTER_STATE_INITIALIZE: - text = "INITIALIZE"; - break; - case MSTP_MASTER_STATE_IDLE: - text = "IDLE"; - break; - case MSTP_MASTER_STATE_USE_TOKEN: - text = "USE_TOKEN"; - break; - case MSTP_MASTER_STATE_WAIT_FOR_REPLY: - text = "WAIT_FOR_REPLY"; - break; - case MSTP_MASTER_STATE_DONE_WITH_TOKEN: - text = "IDLE"; - break; - case MSTP_MASTER_STATE_PASS_TOKEN: - text = "DONE_WITH_TOKEN"; - break; - case MSTP_MASTER_STATE_NO_TOKEN: - text = "NO_TOKEN"; - break; - case MSTP_MASTER_STATE_POLL_FOR_MASTER: - text = "POLL_FOR_MASTER"; - break; - case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: - text = "ANSWER_DATA_REQUEST"; - break; - default: - break; - } - - return text; -} -#endif - -#if PRINT_ENABLED -char *mstp_frame_type_text(int type) -{ - char *text = "unknown"; - - switch (type) { - case FRAME_TYPE_TOKEN: - text = "TOKEN"; - break; - case FRAME_TYPE_POLL_FOR_MASTER: - text = "POLL_FOR_MASTER"; - break; - case FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER: - text = "REPLY_TO_POLL_FOR_MASTER"; - break; - case FRAME_TYPE_TEST_REQUEST: - text = "TEST_REQUEST"; - break; - case FRAME_TYPE_TEST_RESPONSE: - text = "TEST_RESPONSE"; - break; - case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: - text = "BACNET_DATA_EXPECTING_REPLY"; - break; - case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: - text = "BACNET_DATA_NOT_EXPECTING_REPLY"; - break; - case FRAME_TYPE_REPLY_POSTPONED: - text = "REPLY_POSTPONED"; - break; - default: - if ((type >= FRAME_TYPE_PROPRIETARY_MIN) && - (type <= FRAME_TYPE_PROPRIETARY_MAX)) - text = "PROPRIETARY"; - break; - } - - return text; -} -#endif - /* returns true if we need to transition immediately */ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) { @@ -694,7 +586,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) mstp_port->EventCount, mstp_port->TokenCount, mstp_port->SilenceTimer, - mstp_master_state_text(mstp_port->master_state)); + mstptext_master_state(mstp_port->master_state)); } #endif @@ -736,7 +628,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) mstp_port->DataLength, mstp_port->FrameCount, mstp_port->SilenceTimer, - mstp_frame_type_text(mstp_port->FrameType)); + mstptext_frame_type(mstp_port->FrameType)); #endif /* destined for me! */ if ((mstp_port->DestinationAddress == @@ -1139,8 +1031,11 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) /* BACnet Data Expecting Reply, a Test_Request, or */ /* a proprietary frame that expects a reply is received. */ case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: +#if 0 + /* FIXME: we always defer the reply to be safe */ /* FIXME: if we knew the APDU type received, we could - see if the next message was that same APDU type */ + see if the next message was that same APDU type + along with the matching src/dest and invoke ID */ if ((mstp_port->SilenceTimer <= Treply_delay) && mstp_port->TxReady) { /* Reply */ @@ -1159,7 +1054,8 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) mstp_port->TxReady = false; mstp_port->master_state = MSTP_MASTER_STATE_IDLE; } - } + } else +#endif /* DeferredReply */ /* If no reply will be available from the higher layers */ /* within Treply_delay after the reception of the */ @@ -1169,7 +1065,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) /* Any reply shall wait until this node receives the token. */ /* Call MSTP_Create_And_Send_Frame to transmit a Reply Postponed frame, */ /* and enter the IDLE state. */ - else { + { MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_REPLY_POSTPONED, mstp_port->SourceAddress, diff --git a/bacnet-stack/ports/rtos32/mstp.c b/bacnet-stack/ports/rtos32/mstp.c index 5303eabf..77971849 100644 --- a/bacnet-stack/ports/rtos32/mstp.c +++ b/bacnet-stack/ports/rtos32/mstp.c @@ -1166,46 +1166,33 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) /* BACnet Data Expecting Reply, a Test_Request, or */ /* a proprietary frame that expects a reply is received. */ case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: +#if 0 if (mstp_port->ReplyPostponedTimer <= Treply_delay) { - /* Reply */ - /* If a reply is available from the higher layers */ - /* within Treply_delay after the reception of the */ - /* final octet of the requesting frame */ - /* (the mechanism used to determine this is a local matter), */ - /* then call MSTP_Create_And_Send_Frame to transmit the reply frame */ - /* and enter the IDLE state to wait for the next frame. */ + /* FIXME: we always defer the reply to be safe */ + /* FIXME: if we knew the APDU type received, we could + see if the next message was that same APDU type + along with the matching src/dest and invoke ID */ if ((mstp_port->FrameType == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) && (mstp_port->TxReady)) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Create_And_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ RS485_Send_Frame(mstp_port, (uint8_t *) & mstp_port->TxBuffer[0], mstp_port->TxLength); mstp_port->TxReady = false; mstp_port->master_state = MSTP_MASTER_STATE_IDLE; transition_now = true; + } else { + /* Test Request - handled directly in IDLE state */ } - /* Test Request */ - /* If a receiving node can successfully receive and return */ - /* the information field, it shall do so. If it cannot receive */ - /* and return the entire information field but can detect */ - /* the reception of a valid Test_Request frame */ - /* (for example, by computing the CRC on octets as */ - /* they are received), then the receiving node shall discard */ - /* the information field and return a Test_Response containing */ - /* no information field. If the receiving node cannot detect */ - /* the valid reception of frames with overlength information fields, */ - /* then no response shall be returned. */ - else if (mstp_port->FrameType == FRAME_TYPE_TEST_REQUEST) { - MSTP_Create_And_Send_Frame(mstp_port, - FRAME_TYPE_TEST_RESPONSE, - mstp_port->SourceAddress, - mstp_port->This_Station, - (uint8_t *) & mstp_port->InputBuffer[0], - mstp_port->DataLength); - mstp_port->master_state = MSTP_MASTER_STATE_IDLE; - transition_now = true; - } - } + } else +#endif /* DeferredReply */ /* If no reply will be available from the higher layers */ /* within Treply_delay after the reception of the */ @@ -1215,7 +1202,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) /* Any reply shall wait until this node receives the token. */ /* Call MSTP_Create_And_Send_Frame to transmit a Reply Postponed frame, */ /* and enter the IDLE state. */ - else { + { MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_REPLY_POSTPONED, mstp_port->SourceAddress, @@ -1719,7 +1706,6 @@ void testMasterNodeFSM(Test * pTest) MSTP_Init(&mstp_port, my_mac); ct_test(pTest, mstp_port.master_state == MSTP_MASTER_STATE_INITIALIZE); - } #endif diff --git a/bacnet-stack/ports/win32/dlmstp.c b/bacnet-stack/ports/win32/dlmstp.c index 38a62318..cf60c8a5 100644 --- a/bacnet-stack/ports/win32/dlmstp.c +++ b/bacnet-stack/ports/win32/dlmstp.c @@ -42,11 +42,14 @@ /* Number of MS/TP Packets Rx/Tx */ uint16_t MSTP_Packets = 0; -/* receive buffer */ -static DLMSTP_PACKET Receive_Buffer; -/* temp buffer for NPDU insertion */ +/* packet queues */ +static DLMSTP_PACKET Receive_Packet; +static DLMSTP_PACKET Transmit_Packet; /* local MS/TP port data - shared with RS-485 */ volatile struct mstp_port_struct_t MSTP_Port; +/* buffers needed by mstp port struct */ +static uint8_t TxBuffer[MAX_MPDU]; +static uint8_t RxBuffer[MAX_MPDU]; #define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} @@ -76,13 +79,12 @@ int dlmstp_send_pdu(BACNET_ADDRESS * dest, /* destination address */ { /* number of bytes of data */ int bytes_sent = 0; uint8_t destination = 0; /* destination address */ - BACNET_ADDRESS src; - if (MSTP_Port.TxReady == false) { + if (!Transmit_Packet.ready) { if (npdu_data->data_expecting_reply) - MSTP_Port.TxFrameType = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + Transmit_Packet.frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; else - MSTP_Port.TxFrameType = + Transmit_Packet.frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; /* load destination MAC address */ @@ -91,17 +93,17 @@ int dlmstp_send_pdu(BACNET_ADDRESS * dest, /* destination address */ } else { return -2; } - dlmstp_get_my_address(&src); if ((8 /* header len */ + pdu_len) > MAX_MPDU) { return -4; } - bytes_sent = MSTP_Create_Frame( - (uint8_t *) & MSTP_Port.TxBuffer[0], - sizeof(MSTP_Port.TxBuffer), - MSTP_Port.TxFrameType, - destination, MSTP_Port.This_Station, pdu, pdu_len); - MSTP_Port.TxLength = bytes_sent; - MSTP_Port.TxReady = true; + Transmit_Packet.pdu_len = MSTP_Create_Frame( + (uint8_t *) & Transmit_Packet.pdu[0], + sizeof(Transmit_Packet.pdu), + Transmit_Packet.frame_type, + destination, + MSTP_Port.This_Station, + pdu, pdu_len); + Transmit_Packet.ready = true; MSTP_Packets++; } @@ -118,22 +120,22 @@ uint16_t dlmstp_receive( /* see if there is a packet available, and a place to put the reply (if necessary) and process it */ - if (Receive_Buffer.ready) { - if (Receive_Buffer.pdu_len) { + if (Receive_Packet.ready) { + if (Receive_Packet.pdu_len) { MSTP_Packets++; if (src) { memmove(src, - &Receive_Buffer.address, - sizeof(Receive_Buffer.address)); + &Receive_Packet.address, + sizeof(Receive_Packet.address)); } if (pdu) { memmove(pdu, - &Receive_Buffer.pdu, - sizeof(Receive_Buffer.pdu)); + &Receive_Packet.pdu, + sizeof(Receive_Packet.pdu)); } - pdu_len = Receive_Buffer.pdu_len; + pdu_len = Receive_Packet.pdu_len; } - Receive_Buffer.ready = false; + Receive_Packet.ready = false; } return pdu_len; @@ -210,10 +212,10 @@ uint16_t dlmstp_put_receive(uint8_t src, /* source MS/TP address */ uint8_t * pdu, /* PDU data */ uint16_t pdu_len) { /* amount of PDU data */ - /* PDU is already in the Receive_Buffer */ - dlmstp_fill_bacnet_address(&Receive_Buffer.address, src); - Receive_Buffer.pdu_len = pdu_len; - Receive_Buffer.ready = true; + /* PDU is already in the Receive_Packet */ + dlmstp_fill_bacnet_address(&Receive_Packet.address, src); + Receive_Packet.pdu_len = pdu_len; + Receive_Packet.ready = true; return pdu_len; } @@ -229,11 +231,15 @@ uint16_t dlmstp_get_send( uint16_t pdu_len = 0; (void)src; - (void)max_pdu; - if (MSTP_Port.TxReady) { - memmove(&pdu[0],(void *)&MSTP_Port.TxBuffer[0],sizeof(MSTP_Port.TxBuffer)); - pdu_len = MSTP_Port.TxLength; - MSTP_Port.TxReady = false; + (void)timeout; + if (Transmit_Packet.ready) { + if (Transmit_Packet.pdu_len <= max_pdu) { + memmove(&pdu[0], + (void *) & Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len); + pdu_len = Transmit_Packet.pdu_len; + } + Transmit_Packet.ready = false; } return pdu_len; @@ -361,9 +367,9 @@ bool dlmstp_init(char *ifname) unsigned long hThread = 0; uint32_t arg_value = 0; - /* initialize buffer */ - Receive_Buffer.ready = false; - Receive_Buffer.pdu_len = 0; + /* initialize packet queue */ + Receive_Packet.ready = false; + Receive_Packet.pdu_len = 0; /* initialize hardware */ /* initialize hardware */ if (ifname) { @@ -373,6 +379,10 @@ bool dlmstp_init(char *ifname) #endif } RS485_Initialize(); + MSTP_Port.InputBuffer = &RxBuffer[0]; + MSTP_Port.InputBufferSize = sizeof(RxBuffer); + MSTP_Port.OutputBuffer = &TxBuffer[0]; + MSTP_Port.OutputBufferSize = sizeof(TxBuffer); MSTP_Init(&MSTP_Port); #if 0 uint8_t data; diff --git a/bacnet-stack/ports/win32/dlmstp.cbp b/bacnet-stack/ports/win32/dlmstp.cbp index 1383ac20..5ac59a48 100644 --- a/bacnet-stack/ports/win32/dlmstp.cbp +++ b/bacnet-stack/ports/win32/dlmstp.cbp @@ -49,6 +49,14 @@ + + + + + +