modified to support confirmed messages. Verified with ReadProperty request.
This commit is contained in:
@@ -19,6 +19,8 @@ SRCS = ports/linux/main.c \
|
||||
iam.c \
|
||||
rp.c \
|
||||
wp.c \
|
||||
tsm.c \
|
||||
address.c \
|
||||
device.c \
|
||||
ai.c \
|
||||
ao.c \
|
||||
|
||||
+4
-7
@@ -39,6 +39,7 @@
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacenum.h"
|
||||
#include "tsm.h"
|
||||
|
||||
// Confirmed Function Handlers
|
||||
// If they are not set, they are handled by a reject message
|
||||
@@ -244,9 +245,7 @@ void apdu_handler(
|
||||
invoke_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
//FIXME: release the invoke id
|
||||
}
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -297,9 +296,7 @@ void apdu_handler(
|
||||
&service_ack_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
//FIXME: release the invoke id
|
||||
}
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -310,7 +307,7 @@ void apdu_handler(
|
||||
case PDU_TYPE_REJECT:
|
||||
case PDU_TYPE_ABORT:
|
||||
invoke_id = apdu[1];
|
||||
// FIXME: release the invoke id
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -22,4 +22,11 @@
|
||||
// requests available.
|
||||
#define MAX_TSM_TRANSACTIONS 16
|
||||
|
||||
// The address cache is used for binding to BACnet devices
|
||||
// The number of entries corresponds to the number of
|
||||
// devices that might respond to an I-Am on the network.
|
||||
// If your device is a simple server and does not need to bind,
|
||||
// then you don't need to use this.
|
||||
#define MAX_ADDRESS_CACHE 255
|
||||
|
||||
#endif
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
#include "reject.h"
|
||||
#include "abort.h"
|
||||
#include "bacerror.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
|
||||
// Example handlers of services
|
||||
|
||||
@@ -70,6 +72,8 @@
|
||||
|
||||
// flag to send an I-Am
|
||||
bool I_Am_Request = true;
|
||||
// flag to send a global Who-Is
|
||||
bool Who_Is_Request = true;
|
||||
|
||||
// buffers used for transmit and receive
|
||||
static uint8_t Tx_Buf[MAX_MPDU] = {0};
|
||||
@@ -150,6 +154,110 @@ void Send_IAm(void)
|
||||
fprintf(stderr,"Failed to Send I-Am Request (%s)!\n", strerror(errno));
|
||||
}
|
||||
|
||||
void Send_WhoIs(void)
|
||||
{
|
||||
int pdu_len = 0;
|
||||
BACNET_ADDRESS dest;
|
||||
int bytes_sent = 0;
|
||||
|
||||
// Who-Is is a global broadcast
|
||||
bacdl_get_broadcast_address(&dest);
|
||||
|
||||
// encode the NPDU portion of the packet
|
||||
pdu_len = npdu_encode_apdu(
|
||||
&Tx_Buf[0],
|
||||
&dest,
|
||||
NULL,
|
||||
false, // true for confirmed messages
|
||||
MESSAGE_PRIORITY_NORMAL);
|
||||
|
||||
// encode the APDU portion of the packet
|
||||
pdu_len += whois_encode_apdu(
|
||||
&Tx_Buf[pdu_len],
|
||||
-1, // send to all
|
||||
-1);// send to all
|
||||
|
||||
bytes_sent = bacdl_send_pdu(
|
||||
&dest, // destination address
|
||||
&Tx_Buf[0],
|
||||
pdu_len); // number of bytes of data
|
||||
if (bytes_sent > 0)
|
||||
fprintf(stderr,"Sent Who-Is Request!\n");
|
||||
else
|
||||
fprintf(stderr,"Failed to Send Who-Is Request (%s)!\n", strerror(errno));
|
||||
}
|
||||
|
||||
// returns false if device is not bound
|
||||
bool Send_Read_Property_Request(
|
||||
uint32_t device_id, // destination device
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID object_property,
|
||||
int32_t array_index)
|
||||
{
|
||||
BACNET_ADDRESS dest;
|
||||
BACNET_ADDRESS my_address;
|
||||
unsigned max_apdu = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool status = false;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
|
||||
status = address_get_by_device(device_id, &max_apdu, &dest);
|
||||
if (status)
|
||||
{
|
||||
bacdl_get_my_address(&my_address);
|
||||
pdu_len = npdu_encode_apdu(
|
||||
&Tx_Buf[0],
|
||||
&dest,
|
||||
&my_address,
|
||||
true, // true for confirmed messages
|
||||
MESSAGE_PRIORITY_NORMAL);
|
||||
|
||||
invoke_id = tsm_next_free_invokeID();
|
||||
pdu_len += rp_encode_apdu(
|
||||
&Tx_Buf[pdu_len],
|
||||
invoke_id,
|
||||
object_type,
|
||||
object_instance,
|
||||
object_property,
|
||||
array_index);
|
||||
if (pdu_len < max_apdu)
|
||||
{
|
||||
tsm_set_confirmed_unsegmented_transaction(
|
||||
invoke_id,
|
||||
&dest,
|
||||
&Tx_Buf[0],
|
||||
pdu_len);
|
||||
bytes_sent = bacdl_send_pdu(
|
||||
&dest, // destination address
|
||||
&Tx_Buf[0],
|
||||
pdu_len); // number of bytes of data
|
||||
if (bytes_sent > 0)
|
||||
fprintf(stderr,"Sent ReadProperty Request!\n");
|
||||
else
|
||||
fprintf(stderr,"Failed to Send ReadProperty Request (%s)!\n",
|
||||
strerror(errno));
|
||||
}
|
||||
else
|
||||
fprintf(stderr,"Failed to Send ReadProperty Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int handler_send_pdu(
|
||||
BACNET_ADDRESS *dest, // destination address
|
||||
uint8_t *pdu, // any data to be sent - may be null
|
||||
unsigned pdu_len) // number of bytes of data
|
||||
{
|
||||
return bacdl_send_pdu(
|
||||
dest,
|
||||
pdu,
|
||||
pdu_len);
|
||||
}
|
||||
|
||||
void WhoIsHandler(
|
||||
uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
@@ -206,6 +314,29 @@ void IAmHandler(
|
||||
return;
|
||||
}
|
||||
|
||||
void ReadPropertyAckHandler(
|
||||
uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_READ_PROPERTY_DATA data;
|
||||
|
||||
tsm_free_invoke_id(service_data->invoke_id);
|
||||
len = rp_ack_decode_service_request(
|
||||
service_request,
|
||||
service_len,
|
||||
&data);
|
||||
fprintf(stderr,"Received Read-Property Ack!\n");
|
||||
if (len > 0)
|
||||
fprintf(stderr,"type=%u instance=%u property=%u index=%d\n",
|
||||
data.object_type,
|
||||
data.object_instance,
|
||||
data.object_property,
|
||||
data.array_index);
|
||||
}
|
||||
|
||||
void ReadPropertyHandler(
|
||||
uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
|
||||
@@ -33,6 +33,13 @@
|
||||
|
||||
// flag to send an I-Am
|
||||
extern bool I_Am_Request;
|
||||
// flag to send a global Who-Is
|
||||
extern bool Who_Is_Request;
|
||||
|
||||
int handler_send_pdu(
|
||||
BACNET_ADDRESS *dest, // destination address
|
||||
uint8_t *pdu, // any data to be sent - may be null
|
||||
unsigned pdu_len); // number of bytes of data
|
||||
|
||||
void UnrecognizedServiceHandler(
|
||||
uint8_t *service_request,
|
||||
@@ -41,6 +48,7 @@ void UnrecognizedServiceHandler(
|
||||
BACNET_CONFIRMED_SERVICE_DATA *service_data);
|
||||
|
||||
void Send_IAm(void);
|
||||
void Send_WhoIs(void);
|
||||
|
||||
void WhoIsHandler(
|
||||
uint8_t *service_request,
|
||||
@@ -52,6 +60,12 @@ void IAmHandler(
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src);
|
||||
|
||||
void ReadPropertyAckHandler(
|
||||
uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data);
|
||||
|
||||
void ReadPropertyHandler(
|
||||
uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
@@ -64,4 +78,12 @@ void WritePropertyHandler(
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA *service_data);
|
||||
|
||||
// returns false if device is not bound
|
||||
bool Send_Read_Property_Request(
|
||||
uint32_t device_id, // destination device
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID object_property,
|
||||
int32_t array_index);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,12 +28,16 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include "config.h"
|
||||
#include "address.h"
|
||||
#include "bacdef.h"
|
||||
#include "handlers.h"
|
||||
#include "bacdcode.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "iam.h"
|
||||
#include "tsm.h"
|
||||
#include "device.h"
|
||||
#ifdef BACDL_ETHERNET
|
||||
#include "ethernet.h"
|
||||
@@ -133,6 +137,46 @@ static void Init_Device_Parameters(void)
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void LocalIAmHandler(
|
||||
uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src)
|
||||
{
|
||||
int len = 0;
|
||||
uint32_t device_id = 0;
|
||||
unsigned max_apdu = 0;
|
||||
int segmentation = 0;
|
||||
uint16_t vendor_id = 0;
|
||||
|
||||
(void)src;
|
||||
(void)service_len;
|
||||
len = iam_decode_service_request(
|
||||
service_request,
|
||||
&device_id,
|
||||
&max_apdu,
|
||||
&segmentation,
|
||||
&vendor_id);
|
||||
fprintf(stderr,"Received I-Am Request");
|
||||
if (len != -1)
|
||||
{
|
||||
fprintf(stderr," from %u!\n",device_id);
|
||||
address_add(device_id,
|
||||
max_apdu,
|
||||
src);
|
||||
(void)Send_Read_Property_Request(
|
||||
device_id, // destination device
|
||||
OBJECT_DEVICE,
|
||||
device_id,
|
||||
PROP_OBJECT_NAME,
|
||||
BACNET_ARRAY_ALL);
|
||||
}
|
||||
else
|
||||
fprintf(stderr,"!\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void Init_Service_Handlers(void)
|
||||
{
|
||||
@@ -142,7 +186,7 @@ static void Init_Service_Handlers(void)
|
||||
WhoIsHandler);
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_I_AM,
|
||||
IAmHandler);
|
||||
LocalIAmHandler);
|
||||
|
||||
// set the handler for all the services we don't implement
|
||||
// It is required to send the proper reject message...
|
||||
@@ -155,6 +199,34 @@ static void Init_Service_Handlers(void)
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
WritePropertyHandler);
|
||||
// handle the data coming back from confirmed requests
|
||||
apdu_set_confirmed_ack_handler(
|
||||
SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
ReadPropertyAckHandler);
|
||||
}
|
||||
|
||||
static void print_address_cache(void)
|
||||
{
|
||||
unsigned i,j;
|
||||
BACNET_ADDRESS address;
|
||||
uint32_t device_id = 0;
|
||||
unsigned max_apdu = 0;
|
||||
|
||||
fprintf(stderr,"Device\tMAC\tMaxAPDU\tNet\n");
|
||||
for (i = 0; i < MAX_ADDRESS_CACHE; i++)
|
||||
{
|
||||
if (address_get_by_index(i,&device_id, &max_apdu, &address))
|
||||
{
|
||||
fprintf(stderr,"%u\t",device_id);
|
||||
for (j = 0; j < address.mac_len; j++)
|
||||
{
|
||||
fprintf(stderr,"%02X",address.mac[j]);
|
||||
}
|
||||
fprintf(stderr,"\t");
|
||||
fprintf(stderr,"%hu\t",max_apdu);
|
||||
fprintf(stderr,"%hu\n",address.net);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sig_handler(int signo)
|
||||
@@ -166,6 +238,8 @@ static void sig_handler(int signo)
|
||||
bip_cleanup();
|
||||
#endif
|
||||
|
||||
print_address_cache();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -174,7 +248,10 @@ int main(int argc, char *argv[])
|
||||
BACNET_ADDRESS src = {0}; // address where message came from
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 100; // milliseconds
|
||||
|
||||
time_t start_time;
|
||||
time_t new_time = 0;
|
||||
|
||||
start_time = time(NULL); /* get current time */
|
||||
// Linux specials
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGHUP, sig_handler);
|
||||
@@ -193,12 +270,11 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
|
||||
// loop forever
|
||||
for (;;)
|
||||
{
|
||||
// input
|
||||
|
||||
new_time = time(NULL);
|
||||
// returns 0 bytes on timeout
|
||||
#ifdef BACDL_ETHERNET
|
||||
pdu_len = ethernet_receive(
|
||||
@@ -223,10 +299,19 @@ int main(int argc, char *argv[])
|
||||
&Rx_Buf[0],
|
||||
pdu_len);
|
||||
}
|
||||
if (new_time > start_time)
|
||||
{
|
||||
tsm_timer_milliseconds(new_time - start_time * 1000);
|
||||
start_time = new_time;
|
||||
}
|
||||
if (I_Am_Request)
|
||||
{
|
||||
I_Am_Request = false;
|
||||
Send_IAm();
|
||||
} else if (Who_Is_Request)
|
||||
{
|
||||
Who_Is_Request = false;
|
||||
Send_WhoIs();
|
||||
}
|
||||
// output
|
||||
|
||||
|
||||
@@ -64,3 +64,15 @@ make -f ai.mak
|
||||
./analog_input >> test.log
|
||||
make -f ai.mak clean
|
||||
|
||||
make -f wp.mak clean
|
||||
make -f wp.mak
|
||||
./writeproperty >> test.log
|
||||
make -f wp.mak clean
|
||||
|
||||
make -f address.mak clean
|
||||
make -f address.mak
|
||||
./address >> test.log
|
||||
make -f address.mak clean
|
||||
|
||||
|
||||
|
||||
|
||||
+62
-21
@@ -44,6 +44,8 @@
|
||||
#include "tsm.h"
|
||||
#include "config.h"
|
||||
#include "device.h"
|
||||
#include "handlers.h"
|
||||
#include "address.h"
|
||||
|
||||
// Transaction State Machine
|
||||
// Really only needed for segmented messages
|
||||
@@ -135,40 +137,79 @@ uint8_t tsm_next_free_invokeID(void)
|
||||
}
|
||||
|
||||
// returns 0 if there are no free transactions
|
||||
uint8_t tsm_request_confirmed_unsegmented_transaction(
|
||||
void tsm_set_confirmed_unsegmented_transaction(
|
||||
uint8_t invokeID,
|
||||
BACNET_ADDRESS *dest,
|
||||
uint8_t *pdu,
|
||||
uint16_t pdu_len)
|
||||
{
|
||||
uint8_t invokeID = 0;
|
||||
unsigned i = 0, j = 0;
|
||||
uint8_t index;
|
||||
|
||||
|
||||
// see if there is a free transaction
|
||||
for (i = 0; i < MAX_TSM_TRANSACTIONS; i++)
|
||||
if (invokeID)
|
||||
{
|
||||
if (TSM_List[i].state == TSM_STATE_IDLE)
|
||||
index = tsm_find_invokeID_index(invokeID);
|
||||
if (index < MAX_TSM_TRANSACTIONS)
|
||||
{
|
||||
// see if the current invoke id is free
|
||||
invokeID = tsm_next_free_invokeID();
|
||||
if (invokeID)
|
||||
// assign the transaction
|
||||
TSM_List[index].state = TSM_STATE_AWAIT_CONFIRMATION;
|
||||
TSM_List[index].RetryCount = Device_Number_Of_APDU_Retries();
|
||||
// start the timer
|
||||
TSM_List[index].RequestTimer = Device_APDU_Timeout();
|
||||
// copy the data
|
||||
for (j = 0; j < pdu_len; j++)
|
||||
{
|
||||
// assign the transaction
|
||||
TSM_List[i].state = TSM_STATE_AWAIT_CONFIRMATION;
|
||||
TSM_List[i].RetryCount = Device_Number_Of_APDU_Retries();
|
||||
// start the timer
|
||||
TSM_List[i].RequestTimer = Device_APDU_Timeout();
|
||||
// copy the data
|
||||
for (j = 0; j < pdu_len; j++)
|
||||
{
|
||||
TSM_List[i].pdu[j] = pdu[j];
|
||||
}
|
||||
memmove(&TSM_List[i].dest,dest,sizeof(TSM_List[i].dest));
|
||||
TSM_List[index].pdu[j] = pdu[j];
|
||||
}
|
||||
break;
|
||||
TSM_List[index].pdu_len = pdu_len;
|
||||
address_copy(&TSM_List[i].dest,dest);
|
||||
}
|
||||
}
|
||||
|
||||
return invokeID;
|
||||
return;
|
||||
}
|
||||
|
||||
// called once a millisecond
|
||||
void tsm_timer_milliseconds(uint16_t milliseconds)
|
||||
{
|
||||
unsigned i = 0; // counter
|
||||
int bytes_sent = 0;
|
||||
|
||||
for (i = 0; i < MAX_TSM_TRANSACTIONS; i++)
|
||||
{
|
||||
if (TSM_List[i].state == TSM_STATE_AWAIT_CONFIRMATION)
|
||||
{
|
||||
if (TSM_List[i].RequestTimer > milliseconds)
|
||||
TSM_List[i].RequestTimer -= milliseconds;
|
||||
else
|
||||
TSM_List[i].RequestTimer = 0;
|
||||
}
|
||||
// timeout. retry?
|
||||
if (TSM_List[i].RequestTimer == 0)
|
||||
{
|
||||
TSM_List[i].RetryCount--;
|
||||
TSM_List[i].RequestTimer = Device_APDU_Timeout();
|
||||
if (TSM_List[i].RetryCount)
|
||||
{
|
||||
bytes_sent = handler_send_pdu(
|
||||
&TSM_List[i].dest, // destination address
|
||||
&TSM_List[i].pdu[0],
|
||||
TSM_List[i].pdu_len); // number of bytes of data
|
||||
}
|
||||
else
|
||||
TSM_List[i].state = TSM_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tsm_free_invoke_id(uint8_t invokeID)
|
||||
{
|
||||
uint8_t index;
|
||||
|
||||
index = tsm_find_invokeID_index(invokeID);
|
||||
if (index < MAX_TSM_TRANSACTIONS)
|
||||
TSM_List[index].state = TSM_STATE_IDLE;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
+11
-5
@@ -71,7 +71,8 @@ typedef struct BACnet_TSM_Data
|
||||
// used to perform timeout on PDU segments
|
||||
//uint8_t SegmentTimer;
|
||||
// used to perform timeout on Confirmed Requests
|
||||
uint8_t RequestTimer;
|
||||
// in milliseconds
|
||||
uint16_t RequestTimer;
|
||||
// unique id
|
||||
uint8_t InvokeID;
|
||||
// state that the TSM is in
|
||||
@@ -80,17 +81,22 @@ typedef struct BACnet_TSM_Data
|
||||
BACNET_ADDRESS dest;
|
||||
// copy of the PDU, should we need to send it again
|
||||
uint8_t pdu[MAX_PDU];
|
||||
unsigned pdu_len;
|
||||
} BACNET_TSM_DATA;
|
||||
|
||||
bool tsm_transaction_available(void);
|
||||
uint8_t tsm_transaction_idle_count(void);
|
||||
uint8_t tsm_request_confirmed_unsegmented_transaction(
|
||||
void tsm_timer_milliseconds(uint16_t milliseconds);
|
||||
// free the invoke ID when the reply comes back
|
||||
void tsm_free_invoke_id(uint8_t invokeID);
|
||||
// use these in tandem
|
||||
uint8_t tsm_next_free_invokeID(void);
|
||||
// returns the same invoke ID that was given
|
||||
void tsm_set_confirmed_unsegmented_transaction(
|
||||
uint8_t invokeID,
|
||||
BACNET_ADDRESS *dest,
|
||||
uint8_t *pdu,
|
||||
uint16_t pdu_len);
|
||||
void tsm_init_list(BACNET_TSM_DATA *list);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user