Files
bacnet_stack/apps/perl/perl_bindings.c
T
Kari Argillander 369da70f2a Strip tabs and trailing white spaces, and fix end of files (#748)
* format: Strip trailing whitespaces

We want to get rid of trailing whitespaces completly as they make just git
noice. Much better to start using automated tools to get rid of them once and
not getting them back again. This way git history will be cleaner and review
easier.

Commit was generated with:

    pre-commit run --all-files trailing-whitespace

* format: Files should have exactly one new line end of them

It is good practice that every file has one new line. It is not now days so
mandatory but it also is not nice if file has lot of newlines end of it. We will
use pre-commit which takes automatically care about this so let's fix all.

Commit was generated with:

    pre-commit run --all-files end-of-file-fixer

* format: Convert tabs to spaces

Project mostly use spaces over tabs. When mixing tabs and spaces this usually
makes formatting issues and also when changing those in commits it will make lot
of git noise. We will force spaces most of the time and use pre-commit to fix.

Commit was generated with:

    pre-commit run --all-files remove-tabs

---------

Co-authored-by: Kari Argillander <kari.argillander@fidelix.com>
2024-08-25 14:13:57 -05:00

931 lines
31 KiB
C

#include <time.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/basic/services.h"
#include "bacnet/bacenum.h"
#include "bacnet/datalink/datalink.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/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];
snprintf(msg, sizeof(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];
snprintf(msg, sizeof(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];
snprintf(msg, sizeof(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 */
snprintf(ackString, sizeof(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;
snprintf(msg, sizeof(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++) {
snprintf(msg, sizeof(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));
datalink_maintenance_timer(current_seconds - last_seconds);
}
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) {
snprintf(msg, sizeof(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)) {
snprintf(msg, sizeof(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 msg[64];
snprintf(msg, sizeof(msg),
"Failed to Send Time-Synchronization Request (%s)!",
strerror(errno));
LogError(msg);
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;
}