1
0
mirror of https://github.com/stargieg/bacnet-stack synced 2025-10-26 23:35:52 +08:00
bacnet-stack/demo/perl/perl_bindings.c
2013-03-21 22:53:31 +01:00

978 lines
31 KiB
C

#include "bacdef.h"
#include "handlers.h"
#include "bacenum.h"
#include "datalink.h"
#include "device.h"
#include <time.h>
#include "arf.h"
/* Free is redefined as a macro, but Perl does not like that. */
#undef free
/* global variables used in this file */
static uint32_t Target_Device_Object_Instance = 4194303;
static unsigned Target_Max_APDU = 0;
static bool Error_Detected = false;
static BACNET_ADDRESS Target_Address;
static uint8_t Request_Invoke_ID = 0;
static bool isReadPropertyHandlerRegistered = false;
static bool isReadPropertyMultipleHandlerRegistered = false;
static bool isWritePropertyHandlerRegistered = false;
static bool isAtomicWriteFileHandlerRegistered = false;
static bool isAtomicReadFileHandlerRegistered = false;
/****************************************/
/* Logging Support */
/****************************************/
#define MAX_ERROR_STRING 128
#define NO_ERROR "No Error"
static char Last_Error[MAX_ERROR_STRING] = NO_ERROR;
static void LogError(
const char *msg)
{
strcpy(Last_Error, msg);
Error_Detected = true;
}
void BacnetGetError(
SV * errorMsg)
{
sv_setpv(errorMsg, Last_Error);
strcpy(Last_Error, NO_ERROR);
Error_Detected = false;
}
static void __LogAnswer(
const char *msg,
unsigned append)
{
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpv(msg, 0)));
XPUSHs(sv_2mortal(newSViv(append)));
PUTBACK;
call_pv("LogAnswer", G_DISCARD);
FREETMPS;
LEAVE;
}
/**************************************/
/* error handlers */
/*************************************/
static void MyAbortHandler(
BACNET_ADDRESS * src,
uint8_t invoke_id,
uint8_t abort_reason,
bool server)
{
(void) server;
if (address_match(&Target_Address, src) &&
(invoke_id == Request_Invoke_ID)) {
char msg[MAX_ERROR_STRING];
sprintf(msg, "BACnet Abort: %s",
bactext_abort_reason_name((int) abort_reason));
LogError(msg);
}
}
static void MyRejectHandler(
BACNET_ADDRESS * src,
uint8_t invoke_id,
uint8_t reject_reason)
{
if (address_match(&Target_Address, src) &&
(invoke_id == Request_Invoke_ID)) {
char msg[MAX_ERROR_STRING];
sprintf(msg, "BACnet Reject: %s",
bactext_reject_reason_name((int) reject_reason));
LogError(msg);
}
}
static void My_Error_Handler(
BACNET_ADDRESS * src,
uint8_t invoke_id,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code)
{
if (address_match(&Target_Address, src) &&
(invoke_id == Request_Invoke_ID)) {
char msg[MAX_ERROR_STRING];
sprintf(msg, "BACnet Error: %s: %s",
bactext_error_class_name((int) error_class),
bactext_error_code_name((int) error_code));
LogError(msg);
}
}
/**********************************/
/* ACK handlers */
/**********************************/
/*****************************************/
/* Decode the ReadProperty Ack and pass to perl */
/****************************************/
#define MAX_ACK_STRING 512
void rp_ack_extract_data(
BACNET_READ_PROPERTY_DATA * data)
{
char ackString[MAX_ACK_STRING] = "";
char *pAckString = &ackString[0];
BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */
BACNET_APPLICATION_DATA_VALUE value; /* for decode value data */
int len = 0;
uint8_t *application_data;
int application_data_len;
bool first_value = true;
bool print_brace = false;
if (data) {
application_data = data->application_data;
application_data_len = data->application_data_len;
/* FIXME: what if application_data_len is bigger than 255? */
/* value? need to loop until all of the len is gone... */
for (;;) {
len =
bacapp_decode_application_data(application_data,
(uint8_t) application_data_len, &value);
if (first_value && (len < application_data_len)) {
first_value = false;
strncat(pAckString, "{", 1);
pAckString += 1;
print_brace = true;
}
object_value.object_type = data->object_type;
object_value.object_instance = data->object_instance;
object_value.object_property = data->object_property;
object_value.array_index = data->array_index;
object_value.value = &value;
bacapp_snprintf_value(pAckString,
MAX_ACK_STRING - (pAckString - ackString), &object_value);
if (len > 0) {
if (len < application_data_len) {
application_data += len;
application_data_len -= len;
/* there's more! */
strncat(pAckString, ",", 1);
pAckString += 1;
} else {
break;
}
} else {
break;
}
}
if (print_brace) {
strncat(pAckString, "}", 1);
pAckString += 1;
}
/* Now let's call a Perl function to display the data */
__LogAnswer(ackString, 0);
}
}
/*****************************************/
/* Decode the ReadPropertyMultiple Ack and pass to perl */
/****************************************/
void rpm_ack_extract_data(
BACNET_READ_ACCESS_DATA * rpm_data)
{
BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */
BACNET_PROPERTY_REFERENCE *listOfProperties;
BACNET_APPLICATION_DATA_VALUE *value;
bool array_value = false;
char ackString[MAX_ACK_STRING] = "";
char *pAckString = &ackString[0];
if (rpm_data) {
listOfProperties = rpm_data->listOfProperties;
while (listOfProperties) {
value = listOfProperties->value;
if (value) {
if (value->next) {
strncat(pAckString, "{", 1);
pAckString++;
array_value = true;
} else {
array_value = false;
}
object_value.object_type = rpm_data->object_type;
object_value.object_instance = rpm_data->object_instance;
while (value) {
object_value.object_property =
listOfProperties->propertyIdentifier;
object_value.array_index =
listOfProperties->propertyArrayIndex;
object_value.value = value;
bacapp_snprintf_value(pAckString,
MAX_ACK_STRING - (pAckString - ackString),
&object_value);
if (value->next) {
strncat(pAckString, ",", 1);
pAckString++;
} else {
if (array_value) {
strncat(pAckString, "}", 1);
pAckString++;
}
}
value = value->next;
}
} else {
/* AccessError */
sprintf(ackString, "BACnet Error: %s: %s",
bactext_error_class_name((int) listOfProperties->
error.error_class),
bactext_error_code_name((int) listOfProperties->
error.error_code));
LogError(ackString);
}
listOfProperties = listOfProperties->next;
/* Add a separator between consecutive entries so that Perl can */
/* parse this out */
strncat(pAckString, "QQQ", 3);
pAckString += 3;
}
/* Now let's call a Perl function to display the data */
__LogAnswer(ackString, 1);
}
}
static 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;
if (address_match(&Target_Address, src) &&
(service_data->invoke_id == Request_Invoke_ID)) {
len =
arf_ack_decode_service_request(service_request, service_len,
&data);
if (len > 0) {
/* validate the parameters before storing data */
if ((data.access == FILE_STREAM_ACCESS) &&
(service_data->invoke_id == Request_Invoke_ID)) {
char msg[32];
uint8_t *pFileData;
int i;
sprintf(msg, "EOF=%d,start=%d,", data.endOfFile,
data.type.stream.fileStartPosition);
__LogAnswer(msg, 0);
pFileData = octetstring_value(&data.fileData);
for (i = 0; i < octetstring_length(&data.fileData); i++) {
sprintf(msg, "%02x ", *pFileData);
__LogAnswer(msg, 1);
pFileData++;
}
} else {
LogError("Bad stream access reported");
}
}
}
}
/** Handler for a ReadProperty ACK.
* @ingroup DSRP
* Doesn't actually do anything, except, for debugging, to
* print out the ACK data of a matching request.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
static void My_Read_Property_Ack_Handler(
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;
if (address_match(&Target_Address, src) &&
(service_data->invoke_id == Request_Invoke_ID)) {
len =
rp_ack_decode_service_request(service_request, service_len, &data);
if (len > 0) {
rp_ack_extract_data(&data);
}
}
}
/** Handler for a ReadPropertyMultiple ACK.
* @ingroup DSRPM
* For each read property, print out the ACK'd data,
* and free the request data items from linked property list.
*
* @param service_request [in] The contents of the service request.
* @param service_len [in] The length of the service_request.
* @param src [in] BACNET_ADDRESS of the source of the message
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
static void My_Read_Property_Multiple_Ack_Handler(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
{
int len = 0;
BACNET_READ_ACCESS_DATA *rpm_data;
BACNET_READ_ACCESS_DATA *old_rpm_data;
BACNET_PROPERTY_REFERENCE *rpm_property;
BACNET_PROPERTY_REFERENCE *old_rpm_property;
BACNET_APPLICATION_DATA_VALUE *value;
BACNET_APPLICATION_DATA_VALUE *old_value;
if (address_match(&Target_Address, src) &&
(service_data->invoke_id == Request_Invoke_ID)) {
rpm_data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA));
if (rpm_data) {
len =
rpm_ack_decode_service_request(service_request, service_len,
rpm_data);
}
if (len > 0) {
while (rpm_data) {
rpm_ack_extract_data(rpm_data);
rpm_property = rpm_data->listOfProperties;
while (rpm_property) {
value = rpm_property->value;
while (value) {
old_value = value;
value = value->next;
free(old_value);
}
old_rpm_property = rpm_property;
rpm_property = rpm_property->next;
free(old_rpm_property);
}
old_rpm_data = rpm_data;
rpm_data = rpm_data->next;
free(old_rpm_data);
}
} else {
LogError("RPM Ack Malformed! Freeing memory...");
while (rpm_data) {
rpm_property = rpm_data->listOfProperties;
while (rpm_property) {
value = rpm_property->value;
while (value) {
old_value = value;
value = value->next;
free(old_value);
}
old_rpm_property = rpm_property;
rpm_property = rpm_property->next;
free(old_rpm_property);
}
old_rpm_data = rpm_data;
rpm_data = rpm_data->next;
free(old_rpm_data);
}
}
}
}
void My_Write_Property_SimpleAck_Handler(
BACNET_ADDRESS * src,
uint8_t invoke_id)
{
if (address_match(&Target_Address, src) &&
(invoke_id == Request_Invoke_ID)) {
__LogAnswer("WriteProperty Acknowledged!", 0);
}
}
static void Init_Service_Handlers(
)
{
Device_Init(NULL);
/* we need to handle who-is to support dynamic device binding to us */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
/* handle i-am to support binding to other devices */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind);
/* set the handler for all the services we don't implement
It is required to send the proper reject message... */
apdu_set_unrecognized_service_handler_handler
(handler_unrecognized_service);
/* we must implement read property - it's required! */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property);
/* handle generic errors coming back */
apdu_set_abort_handler(MyAbortHandler);
apdu_set_reject_handler(MyRejectHandler);
}
typedef enum {
waitAnswer,
waitBind,
} waitAction;
static void Wait_For_Answer_Or_Timeout(
unsigned timeout_ms,
waitAction action)
{
/* Wait for timeout, failure, or success */
time_t last_seconds = time(NULL);
time_t timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
time_t elapsed_seconds = 0;
uint16_t pdu_len = 0;
BACNET_ADDRESS src = { 0 }; /* address where message came from */
uint8_t Rx_Buf[MAX_MPDU] = { 0 };
while (true) {
time_t current_seconds = time(NULL);
/* If error was detected then bail out */
if (Error_Detected) {
LogError("Some other error occurred");
break;
}
if (elapsed_seconds > timeout_seconds) {
LogError("APDU Timeout");
break;
}
/* Process PDU if one comes in */
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout_ms);
if (pdu_len) {
npdu_handler(&src, &Rx_Buf[0], pdu_len);
}
/* at least one second has passed */
if (current_seconds != last_seconds) {
tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000));
}
if (action == waitAnswer) {
/* Response was received. Exit. */
if (tsm_invoke_id_free(Request_Invoke_ID)) {
break;
} else if (tsm_invoke_id_failed(Request_Invoke_ID)) {
LogError("TSM Timeout!");
tsm_free_invoke_id(Request_Invoke_ID);
break;
}
} else if (action == waitBind) {
if (address_bind_request(Target_Device_Object_Instance,
&Target_Max_APDU, &Target_Address)) {
break;
}
} else {
LogError("Invalid waitAction requested");
break;
}
/* Keep track of time */
elapsed_seconds += (current_seconds - last_seconds);
last_seconds = current_seconds;
}
}
/****************************************************/
/* Interface API */
/****************************************************/
/****************************************************/
/* This is the most fundamental setup needed to start communication */
/****************************************************/
void BacnetPrepareComm(
)
{
/* setup my info */
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
address_init();
Init_Service_Handlers();
dlenv_init();
}
/****************************************************/
/* Try to bind to a device. If successful, return zero. If failure, return */
/* non-zero and log the error details */
/****************************************************/
int BacnetBindToDevice(
int deviceInstanceNumber)
{
int isFailure = 0;
/* Store the requested device instance number in the global variable for */
/* reference in other communication routines */
Target_Device_Object_Instance = deviceInstanceNumber;
/* try to bind with the device */
if (!address_bind_request(deviceInstanceNumber, &Target_Max_APDU,
&Target_Address)) {
Send_WhoIs(Target_Device_Object_Instance,
Target_Device_Object_Instance);
/* Wait for timeout, failure, or success */
Wait_For_Answer_Or_Timeout(100, waitBind);
}
/* Clean up after ourselves */
isFailure = Error_Detected;
Error_Detected = false;
return isFailure;
}
/****************************************************/
/* This is the interface to ReadProperty */
/****************************************************/
int BacnetReadProperty(
int deviceInstanceNumber,
int objectType,
int objectInstanceNumber,
int objectProperty,
int objectIndex)
{
if (!isReadPropertyHandlerRegistered) {
/* handle the data coming back from confirmed requests */
apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY,
My_Read_Property_Ack_Handler);
/* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY,
My_Error_Handler);
/* indicate that handlers are now registered */
isReadPropertyHandlerRegistered = true;
}
/* Send the message out */
Request_Invoke_ID =
Send_Read_Property_Request(deviceInstanceNumber, objectType,
objectInstanceNumber, objectProperty, objectIndex);
Wait_For_Answer_Or_Timeout(100, waitAnswer);
int isFailure = Error_Detected;
Error_Detected = 0;
return isFailure;
}
/************************************************/
/* This is the interface to ReadPropertyMultiple */
/************************************************/
int BacnetReadPropertyMultiple(
int deviceInstanceNumber,
...)
{
/* Get the variable argument list from the stack */
Inline_Stack_Vars;
int rpmIndex = 1;
BACNET_READ_ACCESS_DATA *rpm_object =
calloc(1, sizeof(BACNET_READ_ACCESS_DATA));
BACNET_READ_ACCESS_DATA *Read_Access_Data = rpm_object;
BACNET_PROPERTY_REFERENCE *rpm_property;
uint8_t buffer[MAX_PDU] = { 0 };
while (rpmIndex < Inline_Stack_Items) {
SV *pSV = Inline_Stack_Item(rpmIndex++);
/* Make sure the argument is an Array Reference */
if (SvTYPE(SvRV(pSV)) != SVt_PVAV) {
LogError("Argument is not an Array reference");
break;
}
/* Make sure we can access the memory */
if (rpm_object) {
rpm_object->listOfProperties = NULL;
} else {
LogError("Memory Allocation Issue");
break;
}
AV *pAV = (AV *) SvRV(pSV);
SV **ppSV;
/* The 0th argument is the object type */
ppSV = av_fetch(pAV, 0, 0);
if (ppSV) {
rpm_object->object_type = SvIV(*ppSV);
} else {
LogError("Problem parsing the Array of arguments");
break;
}
/* The 1st argument is the object instance */
ppSV = av_fetch(pAV, 1, 0);
if (ppSV) {
rpm_object->object_instance = SvIV(*ppSV);
} else {
LogError("Problem parsing the Array of arguments");
break;
}
/* The 2nd argument is the property type */
ppSV = av_fetch(pAV, 2, 0);
if (ppSV) {
rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE));
rpm_object->listOfProperties = rpm_property;
if (rpm_property) {
rpm_property->propertyIdentifier = SvIV(*ppSV);
} else {
LogError("Memory allocation error");
break;
}
} else {
LogError("Problem parsing the Array of arguments");
break;
}
/* The 3rd argument is the property index */
ppSV = av_fetch(pAV, 3, 0);
if (ppSV) {
rpm_property->propertyArrayIndex = SvIV(*ppSV);
} else {
LogError("Problem parsing the Array of arguments");
break;
}
/* Advance to the next RPM index */
if (rpmIndex < Inline_Stack_Items) {
rpm_object->next = calloc(1, sizeof(BACNET_READ_ACCESS_DATA));
rpm_object = rpm_object->next;
} else {
rpm_object->next = NULL;
}
}
if (!isReadPropertyMultipleHandlerRegistered) {
/* handle the data coming back from confirmed requests */
apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
My_Read_Property_Multiple_Ack_Handler);
/* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
My_Error_Handler);
/* indicate that handlers are now registered */
isReadPropertyMultipleHandlerRegistered = true;
}
/* Send the message out */
if (!Error_Detected) {
Request_Invoke_ID =
Send_Read_Property_Multiple_Request(&buffer[0], sizeof(buffer),
deviceInstanceNumber, Read_Access_Data);
Wait_For_Answer_Or_Timeout(100, waitAnswer);
}
/* Clean up allocated memory */
BACNET_READ_ACCESS_DATA *old_rpm_object;
BACNET_PROPERTY_REFERENCE *old_rpm_property;
rpm_object = Read_Access_Data;
old_rpm_object = rpm_object;
while (rpm_object) {
rpm_property = rpm_object->listOfProperties;
while (rpm_property) {
old_rpm_property = rpm_property;
rpm_property = rpm_property->next;
free(old_rpm_property);
}
old_rpm_object = rpm_object;
rpm_object = rpm_object->next;
free(old_rpm_object);
}
/* Process the return value */
int isFailure = Error_Detected;
Error_Detected = 0;
return isFailure;
}
/****************************************************/
/* This is the interface to WriteProperty */
/****************************************************/
int BacnetWriteProperty(
int deviceInstanceNumber,
int objectType,
int objectInstanceNumber,
int objectProperty,
int objectPriority,
int objectIndex,
const char *tag,
const char *value)
{
char msg[MAX_ERROR_STRING];
int isFailure = 1;
if (!isWritePropertyHandlerRegistered) {
/* handle the ack coming back */
apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
My_Write_Property_SimpleAck_Handler);
/* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
My_Error_Handler);
/* indicate that handlers are now registered */
isWritePropertyHandlerRegistered = true;
}
if (objectIndex == -1) {
objectIndex = BACNET_ARRAY_ALL;
}
/* Loop for eary exit; */
do {
/* Handle the tag/value pair */
uint8_t context_tag = 0;
BACNET_APPLICATION_TAG property_tag;
BACNET_APPLICATION_DATA_VALUE propertyValue;
if (toupper(tag[0]) == 'C') {
context_tag = strtol(&tag[1], NULL, 0);
propertyValue.context_tag = context_tag;
propertyValue.context_specific = true;
} else {
propertyValue.context_specific = false;
}
property_tag = strtol(tag, NULL, 0);
if (property_tag >= MAX_BACNET_APPLICATION_TAG) {
sprintf(msg, "Error: tag=%u - it must be less than %u",
property_tag, MAX_BACNET_APPLICATION_TAG);
LogError(msg);
break;
}
if (!bacapp_parse_application_data(property_tag, value,
&propertyValue)) {
sprintf(msg, "Error: unable to parse the tag value");
LogError(msg);
break;
}
propertyValue.next = NULL;
/* Send out the message */
Request_Invoke_ID =
Send_Write_Property_Request(deviceInstanceNumber, objectType,
objectInstanceNumber, objectProperty, &propertyValue,
objectPriority, objectIndex);
Wait_For_Answer_Or_Timeout(100, waitAnswer);
/* If we get here, then there were no explicit failures. However, there */
/* could have been implicit failures. Let's look at those also. */
isFailure = Error_Detected;
} while (false);
/* Clean up after ourselves. */
Error_Detected = false;
return isFailure;
}
int BacnetAtomicWriteFile(
int deviceInstanceNumber,
int fileInstanceNumber,
int blockStartAddr,
int blockNumBytes,
char *nibbleBuffer)
{
BACNET_OCTET_STRING fileData;
int i, nibble;
uint8_t byteValue;
unsigned char nibbleValue;
if (!isAtomicWriteFileHandlerRegistered) {
/* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_ATOMIC_WRITE_FILE,
My_Error_Handler);
/* indicate that handlers are now registered */
isAtomicWriteFileHandlerRegistered = true;
}
for (i = 0; i < blockNumBytes; i++) {
byteValue = 0;
for (nibble = 0; nibble < 2; nibble++) {
nibbleValue = toupper(nibbleBuffer[i * 2 + nibble]);
if ((nibbleValue >= '0') && (nibbleValue <= '9')) {
byteValue += (nibbleValue - '0') << (4 * (1 - nibble));
} else if ((nibbleValue >= 'A') && (nibbleValue <= 'F')) {
byteValue += (nibbleValue - 'A' + 10) << (4 * (1 - nibble));
} else {
LogError("Bad data in buffer.");
}
}
fileData.value[i] = byteValue;
}
octetstring_truncate(&fileData, blockNumBytes);
/* Send out the message and wait for answer */
if (!Error_Detected) {
Request_Invoke_ID =
Send_Atomic_Write_File_Stream(deviceInstanceNumber,
fileInstanceNumber, blockStartAddr, &fileData);
Wait_For_Answer_Or_Timeout(100, waitAnswer);
}
int isFailure = Error_Detected;
Error_Detected = 0;
return isFailure;
}
int BacnetGetMaxApdu(
)
{
unsigned requestedOctetCount = 0;
uint16_t my_max_apdu = 0;
/* calculate the smaller of our APDU size or theirs
and remove the overhead of the APDU (varies depending on size).
note: we could fail if there is a bottle neck (router)
and smaller MPDU in betweeen. */
if (Target_Max_APDU < MAX_APDU) {
my_max_apdu = Target_Max_APDU;
} else {
my_max_apdu = MAX_APDU;
}
/* Typical sizes are 50, 128, 206, 480, 1024, and 1476 octets */
if (my_max_apdu <= 50) {
requestedOctetCount = my_max_apdu - 19;
} else if (my_max_apdu <= 480) {
requestedOctetCount = my_max_apdu - 32;
} else if (my_max_apdu <= 1476) {
requestedOctetCount = my_max_apdu - 64;
} else {
requestedOctetCount = my_max_apdu / 2;
}
return requestedOctetCount;
}
int BacnetTimeSync(
int deviceInstanceNumber,
int year,
int month,
int day,
int hour,
int minute,
int second,
int isUTC,
int UTCOffset)
{
BACNET_DATE bdate;
BACNET_TIME btime;
struct tm my_time;
time_t aTime;
struct tm *newTime;
my_time.tm_sec = second;
my_time.tm_min = minute;
my_time.tm_hour = hour;
my_time.tm_mday = day;
my_time.tm_mon = month - 1;
my_time.tm_year = year - 1900;
my_time.tm_wday = 0; /* does not matter */
my_time.tm_yday = 0; /* does not matter */
my_time.tm_isdst = 0; /* does not matter */
aTime = mktime(&my_time);
newTime = localtime(&aTime);
bdate.year = newTime->tm_year;
bdate.month = newTime->tm_mon + 1;
bdate.day = newTime->tm_mday;
bdate.wday = newTime->tm_wday ? newTime->tm_wday : 7;
btime.hour = newTime->tm_hour;
btime.min = newTime->tm_min;
btime.sec = newTime->tm_sec;
btime.hundredths = 0;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
uint8_t Handler_Transmit_Buffer[MAX_PDU] = { 0 };
/* Loop for eary exit */
do {
if (!dcc_communication_enabled()) {
LogError("DCC communicaiton is not enabled");
break;
}
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
datalink_get_my_address(&my_address);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &Target_Address,
&my_address, &npdu_data);
/* encode the APDU portion of the packet */
len =
timesync_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &bdate,
&btime);
pdu_len += len;
/* send it out the datalink */
bytes_sent =
datalink_send_pdu(&Target_Address, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
if (bytes_sent <= 0) {
char errorMsg[64];
sprintf(errorMsg,
"Failed to Send Time-Synchronization Request (%s)!",
strerror(errno));
LogError(errorMsg);
break;
}
Wait_For_Answer_Or_Timeout(100, waitAnswer);
} while (false);
int isFailure = Error_Detected;
Error_Detected = 0;
return isFailure;
}
/****************************************************/
/* This is the interface to AtomicReadFile */
/****************************************************/
int BacnetAtomicReadFile(
int deviceInstanceNumber,
int fileInstanceNumber,
int startOffset,
int numBytes)
{
if (!isAtomicReadFileHandlerRegistered) {
/* handle the data coming back from confirmed requests */
apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE,
AtomicReadFileAckHandler);
/* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE,
My_Error_Handler);
/* indicate that handlers are now registered */
isAtomicReadFileHandlerRegistered = true;
}
/* Send the message out */
Request_Invoke_ID =
Send_Atomic_Read_File_Stream(deviceInstanceNumber, fileInstanceNumber,
startOffset, numBytes);
Wait_For_Answer_Or_Timeout(100, waitAnswer);
int isFailure = Error_Detected;
Error_Detected = 0;
return isFailure;
}