modified for AtomicReadFile handling.
This commit is contained in:
@@ -25,6 +25,9 @@ SRCS = ports/linux/main.c \
|
|||||||
device.c \
|
device.c \
|
||||||
ai.c \
|
ai.c \
|
||||||
ao.c \
|
ao.c \
|
||||||
|
bacfile.c \
|
||||||
|
arf.c \
|
||||||
|
awf.c \
|
||||||
abort.c \
|
abort.c \
|
||||||
reject.c \
|
reject.c \
|
||||||
bacerror.c \
|
bacerror.c \
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@
|
|||||||
#include "bacenum.h"
|
#include "bacenum.h"
|
||||||
#include "config.h" // the custom stuff
|
#include "config.h" // the custom stuff
|
||||||
|
|
||||||
#define MAX_ANALOG_INPUTS 32
|
#define MAX_ANALOG_INPUTS 7
|
||||||
|
|
||||||
// we simply have 0-n object instances. Yours might be
|
// we simply have 0-n object instances. Yours might be
|
||||||
// more complex, and then you need validate that the
|
// more complex, and then you need validate that the
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@
|
|||||||
#include "config.h" // the custom stuff
|
#include "config.h" // the custom stuff
|
||||||
#include "wp.h"
|
#include "wp.h"
|
||||||
|
|
||||||
#define MAX_ANALOG_OUTPUTS 20
|
#define MAX_ANALOG_OUTPUTS 4
|
||||||
|
|
||||||
// we choose to have a NULL level in our system represented by
|
// we choose to have a NULL level in our system represented by
|
||||||
// a particular value. When the priorities are not in use, they
|
// a particular value. When the priorities are not in use, they
|
||||||
|
|||||||
+55
-15
@@ -42,6 +42,25 @@
|
|||||||
#include "tsm.h"
|
#include "tsm.h"
|
||||||
#include "iam.h"
|
#include "iam.h"
|
||||||
|
|
||||||
|
#ifdef TEST
|
||||||
|
void tsm_free_invoke_id(uint8_t invokeID)
|
||||||
|
{
|
||||||
|
// dummy stub for testing
|
||||||
|
(void)invokeID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iam_handler(
|
||||||
|
uint8_t *service_request,
|
||||||
|
uint16_t service_len,
|
||||||
|
BACNET_ADDRESS *src)
|
||||||
|
{
|
||||||
|
// dummy stub for testing
|
||||||
|
(void)service_request;
|
||||||
|
(void)service_len;
|
||||||
|
(void)src;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Confirmed Function Handlers
|
// Confirmed Function Handlers
|
||||||
// If they are not set, they are handled by a reject message
|
// If they are not set, they are handled by a reject message
|
||||||
static confirmed_function
|
static confirmed_function
|
||||||
@@ -149,6 +168,35 @@ void apdu_set_confirmed_ack_handler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t apdu_decode_confirmed_service_request(
|
||||||
|
uint8_t *apdu, // APDU data
|
||||||
|
uint16_t apdu_len,
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA *service_data,
|
||||||
|
uint8_t *service_choice,
|
||||||
|
uint8_t **service_request,
|
||||||
|
uint16_t *service_request_len)
|
||||||
|
{
|
||||||
|
uint16_t len = 0; // counts where we are in PDU
|
||||||
|
|
||||||
|
service_data->segmented_message = (apdu[0] & BIT3) ? true : false;
|
||||||
|
service_data->more_follows = (apdu[0] & BIT2) ? true : false;
|
||||||
|
service_data->segmented_response_accepted = (apdu[0] & BIT1) ? true : false;
|
||||||
|
service_data->max_segs = decode_max_segs(apdu[1]);
|
||||||
|
service_data->max_resp = decode_max_apdu(apdu[1]);
|
||||||
|
service_data->invoke_id = apdu[2];
|
||||||
|
len = 3;
|
||||||
|
if (service_data->segmented_message)
|
||||||
|
{
|
||||||
|
service_data->sequence_number = apdu[len++];
|
||||||
|
service_data->proposed_window_number = apdu[len++];
|
||||||
|
}
|
||||||
|
*service_choice = apdu[len++];
|
||||||
|
*service_request = &apdu[len++];
|
||||||
|
*service_request_len = apdu_len - len;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
void apdu_handler(
|
void apdu_handler(
|
||||||
BACNET_ADDRESS *src, // source address
|
BACNET_ADDRESS *src, // source address
|
||||||
bool data_expecting_reply,
|
bool data_expecting_reply,
|
||||||
@@ -170,21 +218,13 @@ void apdu_handler(
|
|||||||
switch (apdu[0] & 0xF0)
|
switch (apdu[0] & 0xF0)
|
||||||
{
|
{
|
||||||
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
|
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
|
||||||
service_data.segmented_message = (apdu[0] & BIT3) ? true : false;
|
len = apdu_decode_confirmed_service_request(
|
||||||
service_data.more_follows = (apdu[0] & BIT2) ? true : false;
|
&apdu[0], // APDU data
|
||||||
service_data.segmented_response_accepted = (apdu[0] & BIT1) ? true : false;
|
apdu_len,
|
||||||
service_data.max_segs = decode_max_segs(apdu[1]);
|
&service_data,
|
||||||
service_data.max_resp = decode_max_apdu(apdu[1]);
|
&service_choice,
|
||||||
service_data.invoke_id = apdu[2];
|
&service_request,
|
||||||
len = 3;
|
&service_request_len);
|
||||||
if (service_data.segmented_message)
|
|
||||||
{
|
|
||||||
service_data.sequence_number = apdu[len++];
|
|
||||||
service_data.proposed_window_number = apdu[len++];
|
|
||||||
}
|
|
||||||
service_choice = apdu[len++];
|
|
||||||
service_request = &apdu[len++];
|
|
||||||
service_request_len = apdu_len - len;
|
|
||||||
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE)
|
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE)
|
||||||
{
|
{
|
||||||
if (Confirmed_Function[service_choice])
|
if (Confirmed_Function[service_choice])
|
||||||
|
|||||||
@@ -125,6 +125,14 @@ void apdu_set_unconfirmed_handler(
|
|||||||
BACNET_UNCONFIRMED_SERVICE service_choice,
|
BACNET_UNCONFIRMED_SERVICE service_choice,
|
||||||
unconfirmed_function pFunction);
|
unconfirmed_function pFunction);
|
||||||
|
|
||||||
|
uint16_t apdu_decode_confirmed_service_request(
|
||||||
|
uint8_t *apdu, // APDU data
|
||||||
|
uint16_t apdu_len,
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA *service_data,
|
||||||
|
uint8_t *service_choice,
|
||||||
|
uint8_t **service_request,
|
||||||
|
uint16_t *service_request_len);
|
||||||
|
|
||||||
void apdu_handler(
|
void apdu_handler(
|
||||||
BACNET_ADDRESS *src, // source address
|
BACNET_ADDRESS *src, // source address
|
||||||
bool data_expecting_reply,
|
bool data_expecting_reply,
|
||||||
|
|||||||
@@ -1270,7 +1270,7 @@ int decode_bacnet_time(uint8_t * apdu, int *hour, int *min, int *sec,
|
|||||||
int encode_bacnet_date(uint8_t * apdu, int year, int month, int day,
|
int encode_bacnet_date(uint8_t * apdu, int year, int month, int day,
|
||||||
int wday)
|
int wday)
|
||||||
{
|
{
|
||||||
apdu[0] = year;
|
apdu[0] = year - 1900;
|
||||||
apdu[1] = month;
|
apdu[1] = month;
|
||||||
apdu[2] = day;
|
apdu[2] = day;
|
||||||
apdu[3] = wday;
|
apdu[3] = wday;
|
||||||
@@ -1299,7 +1299,7 @@ int encode_tagged_date(uint8_t * apdu, int year, int month, int day,
|
|||||||
// returns the number of apdu bytes consumed
|
// returns the number of apdu bytes consumed
|
||||||
int decode_date(uint8_t * apdu, int *year, int *month, int *day, int *wday)
|
int decode_date(uint8_t * apdu, int *year, int *month, int *day, int *wday)
|
||||||
{
|
{
|
||||||
*year = apdu[0];
|
*year = apdu[0] + 1900;
|
||||||
*month = apdu[1];
|
*month = apdu[1];
|
||||||
*day = apdu[2];
|
*day = apdu[2];
|
||||||
*wday = apdu[3];
|
*wday = apdu[3];
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "config.h" // the custom stuff
|
#include "config.h" // the custom stuff
|
||||||
#include "ai.h" // object list dependency
|
#include "ai.h" // object list dependency
|
||||||
#include "ao.h" // object list dependency
|
#include "ao.h" // object list dependency
|
||||||
|
#include "bacfile.h" // object list dependency
|
||||||
#include "wp.h" // write property handling
|
#include "wp.h" // write property handling
|
||||||
|
|
||||||
static uint32_t Object_Instance_Number = 0;
|
static uint32_t Object_Instance_Number = 0;
|
||||||
@@ -228,6 +229,7 @@ unsigned Device_Object_List_Count(void)
|
|||||||
|
|
||||||
count += Analog_Input_Count();
|
count += Analog_Input_Count();
|
||||||
count += Analog_Output_Count();
|
count += Analog_Output_Count();
|
||||||
|
count += bacfile_count();
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@@ -268,6 +270,17 @@ bool Device_Object_List_Identifier(unsigned array_index,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
object_index -= Analog_Output_Count();
|
||||||
|
if (object_index < bacfile_count())
|
||||||
|
{
|
||||||
|
*object_type = OBJECT_FILE;
|
||||||
|
*instance = bacfile_index_to_instance(object_index);
|
||||||
|
status = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,8 @@
|
|||||||
#include "ao.h"
|
#include "ao.h"
|
||||||
#include "rp.h"
|
#include "rp.h"
|
||||||
#include "wp.h"
|
#include "wp.h"
|
||||||
|
#include "arf.h"
|
||||||
|
#include "bacfile.h"
|
||||||
#include "whois.h"
|
#include "whois.h"
|
||||||
#include "iam.h"
|
#include "iam.h"
|
||||||
#include "reject.h"
|
#include "reject.h"
|
||||||
@@ -411,6 +413,39 @@ void ReadPropertyHandler(
|
|||||||
else
|
else
|
||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
|
case OBJECT_FILE:
|
||||||
|
if (bacfile_valid_instance(object_instance))
|
||||||
|
{
|
||||||
|
len = bacfile_encode_property_apdu(
|
||||||
|
&Temp_Buf[0],
|
||||||
|
object_instance,
|
||||||
|
object_property,
|
||||||
|
array_index,
|
||||||
|
&error_class,
|
||||||
|
&error_code);
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
// encode the APDU portion of the packet
|
||||||
|
rp_data.object_type = object_type;
|
||||||
|
rp_data.object_instance = object_instance;
|
||||||
|
rp_data.object_property = object_property;
|
||||||
|
rp_data.array_index = array_index;
|
||||||
|
rp_data.application_data = &Temp_Buf[0];
|
||||||
|
rp_data.application_data_len = len;
|
||||||
|
// FIXME: probably need a length limitation sent with encode
|
||||||
|
pdu_len += rp_ack_encode_apdu(
|
||||||
|
&Tx_Buf[pdu_len],
|
||||||
|
service_data->invoke_id,
|
||||||
|
&rp_data);
|
||||||
|
fprintf(stderr,"Sending Read Property Ack!\n");
|
||||||
|
send = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
@@ -570,3 +605,175 @@ void WritePropertyHandler(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AtomicReadFileHandler(
|
||||||
|
uint8_t *service_request,
|
||||||
|
uint16_t service_len,
|
||||||
|
BACNET_ADDRESS *src,
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA *service_data)
|
||||||
|
{
|
||||||
|
BACNET_ATOMIC_READ_FILE_DATA data;
|
||||||
|
int len = 0;
|
||||||
|
int pdu_len = 0;
|
||||||
|
BACNET_ADDRESS my_address;
|
||||||
|
bool send = false;
|
||||||
|
bool error = false;
|
||||||
|
int bytes_sent = 0;
|
||||||
|
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
|
||||||
|
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||||
|
char buffer[MAX_APDU - 16] = ""; // for reply data, less apdu overhead
|
||||||
|
|
||||||
|
fprintf(stderr,"Received Atomic-Read-File Request!\n");
|
||||||
|
len = arf_decode_service_request(
|
||||||
|
service_request,
|
||||||
|
service_len,
|
||||||
|
&data);
|
||||||
|
if (len < 0)
|
||||||
|
fprintf(stderr,"Unable to decode Atomic-Read-File Request!\n");
|
||||||
|
// prepare a reply
|
||||||
|
datalink_get_my_address(&my_address);
|
||||||
|
// encode the NPDU portion of the packet
|
||||||
|
pdu_len = npdu_encode_apdu(
|
||||||
|
&Tx_Buf[0],
|
||||||
|
src,
|
||||||
|
&my_address,
|
||||||
|
false, // true for confirmed messages
|
||||||
|
MESSAGE_PRIORITY_NORMAL);
|
||||||
|
// bad decoding - send an abort
|
||||||
|
if (len < 0)
|
||||||
|
{
|
||||||
|
pdu_len += abort_encode_apdu(
|
||||||
|
&Tx_Buf[pdu_len],
|
||||||
|
service_data->invoke_id,
|
||||||
|
ABORT_REASON_OTHER);
|
||||||
|
fprintf(stderr,"Sending Abort!\n");
|
||||||
|
send = true;
|
||||||
|
}
|
||||||
|
else if (service_data->segmented_message)
|
||||||
|
{
|
||||||
|
pdu_len += abort_encode_apdu(
|
||||||
|
&Tx_Buf[pdu_len],
|
||||||
|
service_data->invoke_id,
|
||||||
|
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED);
|
||||||
|
fprintf(stderr,"Sending Abort!\n");
|
||||||
|
send = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (data.access == FILE_STREAM_ACCESS)
|
||||||
|
{
|
||||||
|
data.fileData = &buffer[0];
|
||||||
|
data.fileDataLength = sizeof(buffer);
|
||||||
|
if (data.type.stream.requestedOctetCount < data.fileDataLength)
|
||||||
|
{
|
||||||
|
if (bacfile_read_data(&data))
|
||||||
|
{
|
||||||
|
pdu_len += arf_ack_encode_apdu(
|
||||||
|
&Tx_Buf[pdu_len],
|
||||||
|
service_data->invoke_id,
|
||||||
|
&data);
|
||||||
|
send = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
send = true;
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pdu_len += abort_encode_apdu(
|
||||||
|
&Tx_Buf[pdu_len],
|
||||||
|
service_data->invoke_id,
|
||||||
|
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED);
|
||||||
|
fprintf(stderr,"Sending Abort!\n");
|
||||||
|
send = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error_class = ERROR_CLASS_SERVICES;
|
||||||
|
error_code = ERROR_CODE_INVALID_FILE_ACCESS_METHOD;
|
||||||
|
send = true;
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
pdu_len += bacerror_encode_apdu(
|
||||||
|
&Tx_Buf[pdu_len],
|
||||||
|
service_data->invoke_id,
|
||||||
|
SERVICE_CONFIRMED_ATOMIC_READ_FILE,
|
||||||
|
error_class,
|
||||||
|
error_code);
|
||||||
|
fprintf(stderr,"Sending Error!\n");
|
||||||
|
send = true;
|
||||||
|
}
|
||||||
|
if (send)
|
||||||
|
{
|
||||||
|
bytes_sent = datalink_send_pdu(
|
||||||
|
src, // destination address
|
||||||
|
&Tx_Buf[0],
|
||||||
|
pdu_len); // number of bytes of data
|
||||||
|
if (bytes_sent <= 0)
|
||||||
|
fprintf(stderr,"Failed to send PDU (%s)!\n", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We performed an AtomicReadFile Request,
|
||||||
|
// and here is the data from the server
|
||||||
|
// Note: it does not have to be the same file=instance
|
||||||
|
// that someone can read from us. It is common to
|
||||||
|
// use the description as the file name.
|
||||||
|
void AtomicReadFileAckHandler(
|
||||||
|
uint8_t *service_request,
|
||||||
|
uint16_t service_len,
|
||||||
|
BACNET_ADDRESS *src,
|
||||||
|
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
BACNET_ATOMIC_READ_FILE_DATA data;
|
||||||
|
FILE *pFile = NULL;
|
||||||
|
char *pFilename = NULL;
|
||||||
|
uint32_t instance = 0;
|
||||||
|
|
||||||
|
(void)src;
|
||||||
|
// get the file instance from the tsm data before freeing it
|
||||||
|
instance = bacfile_instance_from_tsm(service_data->invoke_id);
|
||||||
|
tsm_free_invoke_id(service_data->invoke_id);
|
||||||
|
len = arf_ack_decode_service_request(
|
||||||
|
service_request,
|
||||||
|
service_len,
|
||||||
|
&data);
|
||||||
|
fprintf(stderr,"Received Read-File Ack!\n");
|
||||||
|
if ((len > 0) && (instance <= BACNET_MAX_INSTANCE))
|
||||||
|
{
|
||||||
|
// write the data received to the file specified
|
||||||
|
if (data.access == FILE_STREAM_ACCESS)
|
||||||
|
{
|
||||||
|
pFilename = bacfile_name(instance);
|
||||||
|
if (pFilename)
|
||||||
|
{
|
||||||
|
pFile = fopen(pFilename, "rb");
|
||||||
|
if (pFile)
|
||||||
|
{
|
||||||
|
(void)fseek(pFile,
|
||||||
|
data.type.stream.fileStartPosition,
|
||||||
|
SEEK_SET);
|
||||||
|
if (fwrite(data.fileData,data.fileDataLength,1,pFile) != 1)
|
||||||
|
fprintf(stderr,"Failed to write to %s (%u)!\n",
|
||||||
|
pFilename, instance);
|
||||||
|
fclose(pFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (data.access == FILE_RECORD_ACCESS)
|
||||||
|
{
|
||||||
|
// FIXME: add handling for Record Access
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -76,4 +76,15 @@ bool Send_Read_Property_Request(
|
|||||||
BACNET_PROPERTY_ID object_property,
|
BACNET_PROPERTY_ID object_property,
|
||||||
int32_t array_index);
|
int32_t array_index);
|
||||||
|
|
||||||
|
void AtomicReadFileHandler(
|
||||||
|
uint8_t *service_request,
|
||||||
|
uint16_t service_len,
|
||||||
|
BACNET_ADDRESS *src,
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA *service_data);
|
||||||
|
void AtomicReadFileAckHandler(
|
||||||
|
uint8_t *service_request,
|
||||||
|
uint16_t service_len,
|
||||||
|
BACNET_ADDRESS *src,
|
||||||
|
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ void iam_handler(
|
|||||||
int iam_send(uint8_t *buffer);
|
int iam_send(uint8_t *buffer);
|
||||||
|
|
||||||
#ifdef TEST
|
#ifdef TEST
|
||||||
|
#include "ctest.h"
|
||||||
void testIAm(Test * pTest);
|
void testIAm(Test * pTest);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
+50
-37
@@ -176,6 +176,10 @@ int npdu_decode(
|
|||||||
{
|
{
|
||||||
int len = 0; // return value - number of octets loaded in this function
|
int len = 0; // return value - number of octets loaded in this function
|
||||||
int i = 0; // counter
|
int i = 0; // counter
|
||||||
|
uint16_t src_net = 0;
|
||||||
|
uint16_t dest_net = 0;
|
||||||
|
uint8_t address_len = 0;
|
||||||
|
uint8_t mac_octet = 0;
|
||||||
|
|
||||||
if (npdu && npdu_data)
|
if (npdu && npdu_data)
|
||||||
{
|
{
|
||||||
@@ -211,30 +215,35 @@ int npdu_decode(
|
|||||||
// 1 = DNET, DLEN, and Hop Count present
|
// 1 = DNET, DLEN, and Hop Count present
|
||||||
// DLEN = 0 denotes broadcast MAC DADR and DADR field is absent
|
// DLEN = 0 denotes broadcast MAC DADR and DADR field is absent
|
||||||
// DLEN > 0 specifies length of DADR field
|
// DLEN > 0 specifies length of DADR field
|
||||||
if (dest)
|
if (npdu[1] & BIT5)
|
||||||
{
|
{
|
||||||
if (npdu[1] & BIT5)
|
len += decode_unsigned16(&npdu[len], &dest_net);
|
||||||
|
// DLEN = 0 denotes broadcast MAC DADR and DADR field is absent
|
||||||
|
// DLEN > 0 specifies length of DADR field
|
||||||
|
address_len = npdu[len++];
|
||||||
|
if (dest)
|
||||||
{
|
{
|
||||||
len += decode_unsigned16(&npdu[len], &dest->net);
|
dest->net = dest_net;
|
||||||
// DLEN = 0 denotes broadcast MAC DADR and DADR field is absent
|
dest->len = address_len;
|
||||||
// DLEN > 0 specifies length of DADR field
|
}
|
||||||
dest->len = npdu[len++];
|
if (address_len)
|
||||||
if (dest->len)
|
{
|
||||||
|
for (i = 0; i < address_len; i++)
|
||||||
{
|
{
|
||||||
for (i = 0; i < dest->len; i++)
|
mac_octet = npdu[len++];
|
||||||
{
|
if (dest)
|
||||||
dest->adr[i] = npdu[len++];
|
dest->adr[i] = mac_octet;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
// zero out the destination address
|
||||||
|
else if (dest)
|
||||||
|
{
|
||||||
|
dest->net = 0;
|
||||||
|
dest->len = 0;
|
||||||
|
for (i = 0; i < MAX_MAC_LEN; i++)
|
||||||
{
|
{
|
||||||
dest->net = 0;
|
dest->adr[i] = 0;
|
||||||
dest->len = 0;
|
|
||||||
for (i = 0; i < MAX_MAC_LEN; i++)
|
|
||||||
{
|
|
||||||
dest->adr[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Bit 3: Source specifier where:
|
// Bit 3: Source specifier where:
|
||||||
@@ -242,36 +251,40 @@ int npdu_decode(
|
|||||||
// 1 = SNET, SLEN, and SADR present
|
// 1 = SNET, SLEN, and SADR present
|
||||||
// SLEN = 0 Invalid
|
// SLEN = 0 Invalid
|
||||||
// SLEN > 0 specifies length of SADR field
|
// SLEN > 0 specifies length of SADR field
|
||||||
if (src)
|
if (npdu[1] & BIT3)
|
||||||
{
|
{
|
||||||
if (npdu[1] & BIT3)
|
len += decode_unsigned16(&npdu[len], &src_net);
|
||||||
|
// SLEN = 0 denotes broadcast MAC SADR and SADR field is absent
|
||||||
|
// SLEN > 0 specifies length of SADR field
|
||||||
|
address_len = npdu[len++];
|
||||||
|
if (src)
|
||||||
{
|
{
|
||||||
len += decode_unsigned16(&npdu[len], &src->net);
|
src->net = src_net;
|
||||||
// SLEN = 0 denotes broadcast MAC SADR and SADR field is absent
|
src->len = address_len;
|
||||||
// SLEN > 0 specifies length of SADR field
|
}
|
||||||
src->len = npdu[len++];
|
if (address_len)
|
||||||
if (src->len)
|
{
|
||||||
|
for (i = 0; i < address_len; i++)
|
||||||
{
|
{
|
||||||
for (i = 0; i < src->len; i++)
|
mac_octet = npdu[len++];
|
||||||
{
|
if (src)
|
||||||
src->adr[i] = npdu[len++];
|
src->adr[i] = mac_octet;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else if (src)
|
||||||
|
{
|
||||||
|
src->net = 0;
|
||||||
|
src->len = 0;
|
||||||
|
for (i = 0; i < MAX_MAC_LEN; i++)
|
||||||
{
|
{
|
||||||
src->net = 0;
|
src->adr[i] = 0;
|
||||||
src->len = 0;
|
|
||||||
for (i = 0; i < MAX_MAC_LEN; i++)
|
|
||||||
{
|
|
||||||
src->adr[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The Hop Count field shall be present only if the message is
|
// The Hop Count field shall be present only if the message is
|
||||||
// destined for a remote network, i.e., if DNET is present.
|
// destined for a remote network, i.e., if DNET is present.
|
||||||
// This is a one-octet field that is initialized to a value of 0xff.
|
// This is a one-octet field that is initialized to a value of 0xff.
|
||||||
if (dest && dest->net)
|
if (dest_net)
|
||||||
npdu_data->hop_count = npdu[len++];
|
npdu_data->hop_count = npdu[len++];
|
||||||
else
|
else
|
||||||
npdu_data->hop_count = 0;
|
npdu_data->hop_count = 0;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "iam.h"
|
#include "iam.h"
|
||||||
#include "tsm.h"
|
#include "tsm.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
|
#include "bacfile.h"
|
||||||
#ifdef BACDL_ETHERNET
|
#ifdef BACDL_ETHERNET
|
||||||
#include "ethernet.h"
|
#include "ethernet.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -236,6 +237,7 @@ static void Init_Service_Handlers(void)
|
|||||||
// It is required to send the proper reject message...
|
// It is required to send the proper reject message...
|
||||||
apdu_set_unrecognized_service_handler_handler(
|
apdu_set_unrecognized_service_handler_handler(
|
||||||
UnrecognizedServiceHandler);
|
UnrecognizedServiceHandler);
|
||||||
|
// Set the handlers for any confirmed services that we support
|
||||||
// we must implement read property - it's required!
|
// we must implement read property - it's required!
|
||||||
apdu_set_confirmed_handler(
|
apdu_set_confirmed_handler(
|
||||||
SERVICE_CONFIRMED_READ_PROPERTY,
|
SERVICE_CONFIRMED_READ_PROPERTY,
|
||||||
@@ -243,10 +245,16 @@ static void Init_Service_Handlers(void)
|
|||||||
apdu_set_confirmed_handler(
|
apdu_set_confirmed_handler(
|
||||||
SERVICE_CONFIRMED_WRITE_PROPERTY,
|
SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||||
WritePropertyHandler);
|
WritePropertyHandler);
|
||||||
|
apdu_set_confirmed_handler(
|
||||||
|
SERVICE_CONFIRMED_ATOMIC_READ_FILE,
|
||||||
|
AtomicReadFileHandler);
|
||||||
// handle the data coming back from confirmed requests
|
// handle the data coming back from confirmed requests
|
||||||
apdu_set_confirmed_ack_handler(
|
apdu_set_confirmed_ack_handler(
|
||||||
SERVICE_CONFIRMED_READ_PROPERTY,
|
SERVICE_CONFIRMED_READ_PROPERTY,
|
||||||
ReadPropertyAckHandler);
|
ReadPropertyAckHandler);
|
||||||
|
apdu_set_confirmed_ack_handler(
|
||||||
|
SERVICE_CONFIRMED_ATOMIC_READ_FILE,
|
||||||
|
AtomicReadFileAckHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_address_cache(void)
|
static void print_address_cache(void)
|
||||||
|
|||||||
+34
-2
@@ -137,7 +137,6 @@ uint8_t tsm_next_free_invokeID(void)
|
|||||||
return invokeID;
|
return invokeID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns 0 if there are no free transactions
|
|
||||||
void tsm_set_confirmed_unsegmented_transaction(
|
void tsm_set_confirmed_unsegmented_transaction(
|
||||||
uint8_t invokeID,
|
uint8_t invokeID,
|
||||||
BACNET_ADDRESS *dest,
|
BACNET_ADDRESS *dest,
|
||||||
@@ -147,7 +146,6 @@ void tsm_set_confirmed_unsegmented_transaction(
|
|||||||
uint16_t j = 0;
|
uint16_t j = 0;
|
||||||
uint8_t index;
|
uint8_t index;
|
||||||
|
|
||||||
|
|
||||||
if (invokeID)
|
if (invokeID)
|
||||||
{
|
{
|
||||||
index = tsm_find_invokeID_index(invokeID);
|
index = tsm_find_invokeID_index(invokeID);
|
||||||
@@ -171,6 +169,40 @@ void tsm_set_confirmed_unsegmented_transaction(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// used to retrieve the transaction payload
|
||||||
|
// if we wanted to find out what we sent (i.e. when we get an ack)
|
||||||
|
bool tsm_get_transaction_pdu(
|
||||||
|
uint8_t invokeID,
|
||||||
|
BACNET_ADDRESS *dest,
|
||||||
|
uint8_t *pdu,
|
||||||
|
uint16_t *pdu_len)
|
||||||
|
{
|
||||||
|
uint16_t j = 0;
|
||||||
|
uint8_t index;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
if (invokeID)
|
||||||
|
{
|
||||||
|
index = tsm_find_invokeID_index(invokeID);
|
||||||
|
// how much checking is needed? state? dest match? just invokeID?
|
||||||
|
if (index < MAX_TSM_TRANSACTIONS)
|
||||||
|
{
|
||||||
|
// FIXME: we may want to free the transaction so it doesn't timeout
|
||||||
|
// retrieve the transaction
|
||||||
|
// FIXME: bounds check the pdu_len?
|
||||||
|
*pdu_len = TSM_List[index].pdu_len;
|
||||||
|
for (j = 0; j < *pdu_len; j++)
|
||||||
|
{
|
||||||
|
pdu[j] = TSM_List[index].pdu[j];
|
||||||
|
}
|
||||||
|
address_copy(dest,&TSM_List[index].dest);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
// called once a millisecond
|
// called once a millisecond
|
||||||
void tsm_timer_milliseconds(uint16_t milliseconds)
|
void tsm_timer_milliseconds(uint16_t milliseconds)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -97,6 +97,12 @@ void tsm_set_confirmed_unsegmented_transaction(
|
|||||||
BACNET_ADDRESS *dest,
|
BACNET_ADDRESS *dest,
|
||||||
uint8_t *pdu,
|
uint8_t *pdu,
|
||||||
uint16_t pdu_len);
|
uint16_t pdu_len);
|
||||||
|
// returns true if transaction is found
|
||||||
|
bool tsm_get_transaction_pdu(
|
||||||
|
uint8_t invokeID,
|
||||||
|
BACNET_ADDRESS *dest,
|
||||||
|
uint8_t *pdu,
|
||||||
|
uint16_t *pdu_len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user