Feature/comments and buffer checks (#73)

* Added comments

* Replaced Goto construct

* Added apdu_len check

* Added comments

* Added string limit and reworked printable check

* Mainly comments

* Just comments

* Just comments

* decode service request returns now non zero on success

* eliminated warnings

* Added character string init with length check.

* Paranoic length check

* Comments and object index checking on read/write.

* Check name/desc strings before returning.

* Eliminated Goto
This commit is contained in:
Roy Schneider
2020-04-16 17:38:49 +02:00
committed by GitHub
parent 1ead6acea5
commit 8f13d59629
16 changed files with 987 additions and 360 deletions
+5 -3
View File
@@ -422,7 +422,9 @@ static int get_local_ifr_ioctl(char *ifname, struct ifreq *ifr, int request)
int fd;
int rv; /* return value */
strncpy(ifr->ifr_name, ifname, sizeof(ifr->ifr_name));
strncpy(ifr->ifr_name, ifname, sizeof(ifr->ifr_name) - 1);
ifr->ifr_name[sizeof(ifr->ifr_name) - 1] = 0;
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (fd < 0) {
rv = fd;
@@ -443,7 +445,7 @@ static int get_local_ifr_ioctl(char *ifname, struct ifreq *ifr, int request)
*/
int bip_get_local_address_ioctl(char *ifname, struct in_addr *addr, int request)
{
struct ifreq ifr = { { { 0 } } };
struct ifreq ifr = { {{0}}, {{0}} };
struct sockaddr_in *tcpip_address;
int rv; /* return value */
@@ -535,7 +537,7 @@ bool bip_init(char *ifname)
struct sockaddr_in sin;
int sockopt = 0;
int sock_fd = -1;
char *ifname_default = "eth0";
static char *ifname_default = "eth0";
if (ifname) {
bip_set_interface(ifname);
+30 -4
View File
@@ -133,7 +133,17 @@ BACNET_ERROR_CODE abort_convert_to_error_code(BACNET_ABORT_REASON abort_code)
return (error_code);
}
/* encode service */
/**
* @brief Encode the BACnet Abort Service, returning the reason
* for the operation being aborted.
*
* @param apdu Transmit buffer
* @param invoke_id ID invoked
* @param abort_reason Abort reason, see ABORT_REASON_X enumeration for details
* @param server True, if the abort has been issued by this device.
*
* @return Total length of the apdu, typically 3 on success, zero otherwise.
*/
int abort_encode_apdu(
uint8_t *apdu, uint8_t invoke_id, uint8_t abort_reason, bool server)
{
@@ -154,7 +164,17 @@ int abort_encode_apdu(
}
#if !BACNET_SVC_SERVER
/* decode the service request only */
/**
* @brief Decode the BACnet Abort Service, returning the reason
* for the operation being aborted.
*
* @param apdu Receive buffer
* @param apdu_len Count of bytes valid in the received buffer.
* @param invoke_id Pointer to a variable, taking the invoked ID from the message.
* @param abort_reason Pointer to a variable, taking the abort reason, see ABORT_REASON_X enumeration for details
*
* @return Total length of the apdu, typically 2 on success, zero otherwise.
*/
int abort_decode_service_request(
uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id, uint8_t *abort_reason)
{
@@ -164,8 +184,14 @@ int abort_decode_service_request(
if (invoke_id) {
*invoke_id = apdu[0];
}
if (abort_reason) {
*abort_reason = apdu[1];
len++;
if (apdu_len > 1) {
if (abort_reason) {
*abort_reason = apdu[1];
}
len++;
}
}
+27 -15
View File
@@ -37,11 +37,15 @@
/** @file alarm_ack.c Handles Event Notifications (ACKs) */
/***************************************************
**
** Creates an Unconfirmed Event Notification APDU
**
****************************************************/
/**
* @brief Creates an Unconfirmed Event Notification APDU
*
* @param apdu Transmission buffer
* @param invoke_id Invoked ID
* @param data Alarm acknowledge data that will be copied into the payload.
*
* @return Length of the APDU
*/
int alarm_ack_encode_apdu(
uint8_t *apdu, uint8_t invoke_id, BACNET_ALARM_ACK_DATA *data)
{
@@ -62,11 +66,15 @@ int alarm_ack_encode_apdu(
return apdu_len;
}
/***************************************************
**
** Encodes the service data part of Event Notification
**
****************************************************/
/**
* @brief Encodes the service data part of Event Notification.
*
* @param apdu Receive buffer
* @param data Alarm acknowledge data that will be filled in
* with the data from the payload.
*
* @return Total length of the apdu
*/
int alarm_ack_encode_service_request(uint8_t *apdu, BACNET_ALARM_ACK_DATA *data)
{
int len = 0; /* length of each encoding */
@@ -102,11 +110,15 @@ int alarm_ack_encode_service_request(uint8_t *apdu, BACNET_ALARM_ACK_DATA *data)
return apdu_len;
}
/***************************************************
**
** Decodes the service data part of Event Notification
**
****************************************************/
/**
* @brief Decodes the service data part of Event Notification.
*
* @param apdu Receive buffer
* @param apdu_len Bytes valid in the buffer.
* @param data Alarm acknowledge data that will be filled.
*
* @return Total length of the apdu
*/
int alarm_ack_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_ALARM_ACK_DATA *data)
{
+12 -2
View File
@@ -158,8 +158,18 @@ int bacapp_encode_application_data(
return apdu_len;
}
/* decode the data and store it into value.
Return the number of octets consumed. */
/**
* @brief Decode the data and store it into value.
* Return the number of octets consumed.
*
* @param apdu Receive buffer
* @param tag_data_type Data type of the given tag
* @param len_value_type Count of bytes of given tag
* @param value Pointer to the application value structure,
* used to store the decoded value to.
*
* @return Number of octets consumed.
*/
int bacapp_decode_data(uint8_t *apdu,
uint8_t tag_data_type,
uint32_t len_value_type,
+56 -14
View File
@@ -1981,9 +1981,15 @@ int encode_context_signed(uint8_t *apdu, uint8_t tag_number, int32_t value)
}
#endif
/* from clause 20.2.6 Encoding of a Real Number Value */
/* and 20.2.1 General Rules for Encoding BACnet Tags */
/* returns the number of apdu bytes consumed */
/**
* @brief Encode a real floating value. From clause 20.2.6 Encoding of a
* Real Number Value and 20.2.1 General Rules for Encoding BACnet Tags.
*
* @param apdu Transmit buffer
* @param value The float value to be encoded.
*
* @return Returns the number of apdu bytes consumed.
*/
int encode_application_real(uint8_t *apdu, float value)
{
int len = 0;
@@ -1996,6 +2002,17 @@ int encode_application_real(uint8_t *apdu, float value)
return len;
}
/**
* @brief Encode a real floating value with a given tag. From clause 20.2.6
* Encoding of a Real Number Value and 20.2.1 General Rules for
* Encoding BACnet Tags.
*
* @param apdu Transmit buffer
* @param tag_number Tag number to be used
* @param value The float value to be encoded.
*
* @return Returns the number of apdu bytes consumed.
*/
int encode_context_real(uint8_t *apdu, uint8_t tag_number, float value)
{
int len = 0;
@@ -2356,7 +2373,15 @@ int decode_context_date(uint8_t *apdu, uint8_t tag_number, BACNET_DATE *bdate)
return len;
}
/* returns the number of apdu bytes consumed */
/**
* Encode a simple ACK and returns the number of apdu bytes consumed.
*
* @param apdu Transmit buffer
* @param invoke_id ID invoked
* @param service_choice Service being acked
*
* @return number of apdu bytes consumed
*/
int encode_simple_ack(uint8_t *apdu, uint8_t invoke_id, uint8_t service_choice)
{
apdu[0] = PDU_TYPE_SIMPLE_ACK;
@@ -2366,24 +2391,41 @@ int encode_simple_ack(uint8_t *apdu, uint8_t invoke_id, uint8_t service_choice)
return 3;
}
/* BACnetAddress */
/**
* Encode a BACnetAddress and returns the number of apdu bytes consumed.
*
* @param apdu Transmit buffer
* @param destination Pointer to the destination address to be encoded.
*
* @return number of apdu bytes created
*/
int encode_bacnet_address(uint8_t *apdu, BACNET_ADDRESS *destination)
{
int apdu_len = 0;
BACNET_OCTET_STRING mac_addr;
/* network number */
apdu_len += encode_application_unsigned(&apdu[apdu_len], destination->net);
/* encode mac address as an octet-string */
if (destination->len != 0) {
octetstring_init(&mac_addr, destination->adr, destination->len);
} else {
octetstring_init(&mac_addr, destination->mac, destination->mac_len);
if (destination) {
/* network number */
apdu_len += encode_application_unsigned(&apdu[apdu_len], destination->net);
/* encode mac address as an octet-string */
if (destination->len != 0) {
octetstring_init(&mac_addr, destination->adr, destination->len);
} else {
octetstring_init(&mac_addr, destination->mac, destination->mac_len);
}
apdu_len += encode_application_octet_string(&apdu[apdu_len], &mac_addr);
}
apdu_len += encode_application_octet_string(&apdu[apdu_len], &mac_addr);
return apdu_len;
}
/* BACnetAddress */
/**
* Dencode a BACnetAddress and returns the number of apdu bytes consumed.
*
* @param apdu Receive buffer
* @param destination Pointer to the destination address structure to be filled in.
*
* @return number of apdu bytes consumed
*/
int decode_bacnet_address(uint8_t *apdu, BACNET_ADDRESS *destination)
{
int len = 0;
+80 -23
View File
@@ -299,6 +299,15 @@ bool characterstring_init(BACNET_CHARACTER_STRING *char_string,
return status;
}
bool characterstring_init_ansi_safe(
BACNET_CHARACTER_STRING * char_string,
const char *value,
size_t tmax)
{
return characterstring_init(char_string, CHARACTER_ANSI_X34, value,
value ? strnlen(value, tmax) : 0);
}
bool characterstring_init_ansi(
BACNET_CHARACTER_STRING *char_string, const char *value)
{
@@ -431,7 +440,13 @@ bool characterstring_truncate(
return status;
}
/* Returns the value. */
/**
* @brief Returns the pointer to the C-string for the given BACnet string.
*
* @param char_string Pointer to the character string.
*
* @return Pointer to a zero-terminated C-string.
*/
char *characterstring_value(BACNET_CHARACTER_STRING *char_string)
{
char *value = NULL;
@@ -443,19 +458,37 @@ char *characterstring_value(BACNET_CHARACTER_STRING *char_string)
return value;
}
/* returns the length. */
/**
* @brief Returns the length for the given BACnet string.
*
* @param char_string Pointer to the character string.
*
* @return Length of the charcater string, but
* maximum MAX_CHARACTER_STRING_BYTES.
*/
size_t characterstring_length(BACNET_CHARACTER_STRING *char_string)
{
size_t length = 0;
if (char_string) {
/* FIXME: validate length is within bounds? */
length = char_string->length;
/* Length within bounds? */
if (length > CHARACTER_STRING_CAPACITY) {
length = CHARACTER_STRING_CAPACITY;
}
}
return length;
}
/**
* @brief Returns the possible capacity for the given BACnet string.
*
* @param char_string Pointer to the character string.
*
* @return MAX_CHARACTER_STRING_BYTES
*/
size_t characterstring_capacity(BACNET_CHARACTER_STRING *char_string)
{
size_t length = 0;
@@ -467,7 +500,13 @@ size_t characterstring_capacity(BACNET_CHARACTER_STRING *char_string)
return length;
}
/* returns the encoding. */
/**
* @brief Returns the character encoding for the given BACnet string.
*
* @param char_string Pointer to the character string.
*
* @return Encoding, like CHARACTER_ANSI_X34
*/
uint8_t characterstring_encoding(BACNET_CHARACTER_STRING *char_string)
{
uint8_t encoding = 0;
@@ -479,7 +518,14 @@ uint8_t characterstring_encoding(BACNET_CHARACTER_STRING *char_string)
return encoding;
}
/* returns the encoding. */
/**
* @brief Set the character encoding for the given BACnet string.
*
* @param char_string Pointer to the character string.
* @param Encoding, like CHARACTER_ANSI_X34
*
* @return true/false on error
*/
bool characterstring_set_encoding(
BACNET_CHARACTER_STRING *char_string, uint8_t encoding)
{
@@ -493,32 +539,43 @@ bool characterstring_set_encoding(
return status;
}
/* returns true if string is printable */
/* used to assist in the requirement that
"The set of characters used in the Object_Name shall be
restricted to printable characters." */
/* printable character: a character that represents a printable
symbol as opposed to a device control character. These
include, but are not limited to, upper- and lowercase letters,
punctuation marks, and mathematical symbols. The exact set
depends upon the character set being used. In ANSI X3.4 the
printable characters are represented by single octets in the range
X'20' - X'7E'.*/
/**
* @brief Returns true if string is printable.
*
* Used to assist in the requirement that
* "The set of characters used in the Object_Name shall be
* restricted to printable characters."
*
* Printable character: a character that represents a printable
* symbol as opposed to a device control character. These
* include, but are not limited to, upper- and lowercase letters,
* punctuation marks, and mathematical symbols. The exact set
* depends upon the character set being used. In ANSI X3.4 the
* printable characters are represented by single octets in the range
* X'20' - X'7E'.
*
* @param char_string Pointer to the character string.
*
* @return true/false on error
*/
bool characterstring_printable(BACNET_CHARACTER_STRING *char_string)
{
bool status = false; /* return value */
size_t i; /* counter */
size_t imax;
char chr;
if (char_string) {
if (char_string->encoding == CHARACTER_ANSI_X34) {
status = true;
for (i = 0; i < MAX_CHARACTER_STRING_BYTES; i++) {
if (i < char_string->length) {
if ((char_string->value[i] < 0x20) ||
(char_string->value[i] > 0x7E)) {
status = false;
}
} else {
imax = char_string->length;
if (imax > CHARACTER_STRING_CAPACITY) {
imax = CHARACTER_STRING_CAPACITY;
}
for (i = 0; i < imax; i++) {
chr = char_string->value[i];
if ((chr < 0x20) || (chr > 0x7E)) {
status = false;
break;
}
}
+4
View File
@@ -120,6 +120,10 @@ extern "C" {
bool characterstring_init_ansi(
BACNET_CHARACTER_STRING * char_string,
const char *value);
bool characterstring_init_ansi_safe(
BACNET_CHARACTER_STRING * char_string,
const char *value,
size_t tmax);
bool characterstring_copy(
BACNET_CHARACTER_STRING * dest,
BACNET_CHARACTER_STRING * src);
+28 -8
View File
@@ -94,11 +94,23 @@ void address_own_device_id_set(uint32_t own_id)
Own_Device_ID = own_id;
}
/**
* @brief Check if the given source and destination address can be matched
* by checking the length, net and MAC address.
*
* @param dest Destination address
* @param src Source address
*
* @return true for a match, false otherwise
*/
bool address_match(BACNET_ADDRESS *dest, BACNET_ADDRESS *src)
{
uint8_t i = 0;
uint8_t max_len = 0;
if (dest == src) {
return(true);
}
if (dest->mac_len != src->mac_len) {
return false;
}
@@ -136,6 +148,11 @@ bool address_match(BACNET_ADDRESS *dest, BACNET_ADDRESS *src)
return true;
}
/**
* @brief Remove a device from the address list.
*
* @param device_id ID of the device
*/
void address_remove_device(uint32_t device_id)
{
struct Address_Cache_Entry *pMatch;
@@ -158,13 +175,15 @@ void address_remove_device(uint32_t device_id)
return;
}
/*****************************************************************************
* Search the cache for the entry nearest expiry and delete it. Mark the *
* entry as reserved with a 1 hour TTL and return a pointer to the reserved *
* entry. Will not delete a static entry and returns NULL pointer if no *
* entry available to free up. Does not check for free entries as it is *
* assumed we are calling this due to the lack of those. *
*****************************************************************************/
/**
* @brief Search the cache for the entry nearest expiry and delete it. Mark the
* entry as reserved with a 1 hour TTL and return a pointer to the reserved
* entry. Will not delete a static entry and returns NULL pointer if no
* entry available to free up. Does not check for free entries as it is
* assumed we are calling this due to the lack of those.
*
* @return Candidate for deletation.
*/
static struct Address_Cache_Entry *address_remove_oldest(void)
{
@@ -224,7 +243,8 @@ static struct Address_Cache_Entry *address_remove_oldest(void)
return (pCandidate);
}
/** Initialize a BACNET_MAC_ADDRESS
/**
* Initialize a BACNET_MAC_ADDRESS
*
* @param mac [out] BACNET_MAC_ADDRESS structure
* @param adr [in] address to initialize, null if empty
+4
View File
@@ -70,6 +70,10 @@ void npdu_handler(BACNET_ADDRESS *src, /* source address */
BACNET_ADDRESS dest = { 0 };
BACNET_NPDU_DATA npdu_data = { 0 };
if (pdu_len < 1) {
return;
}
/* only handle the version that we know how to handle */
if (pdu[0] == BACNET_PROTOCOL_VERSION) {
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data);
+113 -24
View File
@@ -63,6 +63,13 @@ static const int Analog_Value_Properties_Optional[] = { PROP_DESCRIPTION,
static const int Analog_Value_Properties_Proprietary[] = { -1 };
/**
* Initialize the pointers for the required, the optional and the properitary value properties.
*
* @param pRequired - Pointer to the pointer of required values.
* @param pOptional - Pointer to the pointer of optional values.
* @param pProprietary - Pointer to the pointer of properitary values.
*/
void Analog_Value_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
@@ -79,6 +86,9 @@ void Analog_Value_Property_Lists(
return;
}
/**
* Initialize the analog values.
*/
void Analog_Value_Init(void)
{
unsigned i;
@@ -116,9 +126,15 @@ void Analog_Value_Init(void)
}
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
/**
* We simply have 0-n object instances. Yours might be
* more complex, and then you need validate that the
* given instance exists.
*
* @param object_instance Object instance
*
* @return true/false
*/
bool Analog_Value_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_ANALOG_VALUES) {
@@ -128,24 +144,39 @@ bool Analog_Value_Valid_Instance(uint32_t object_instance)
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
/**
* Return the count of analog values.
*
* @return Count of analog values.
*/
unsigned Analog_Value_Count(void)
{
return MAX_ANALOG_VALUES;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
/**
* We simply have 0-n object instances. Yours might be
* more complex, and then you need to return the instance
* that correlates to the correct index.
*
* @param index Index
*
* @return Object instance
*/
uint32_t Analog_Value_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
/**
* We simply have 0-n object instances. Yours might be
* more complex, and then you need to return the index
* that correlates to the correct instance number
*
* @param object_instance Object instance
*
* @return Index in the object table.
*/
unsigned Analog_Value_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_ANALOG_VALUES;
@@ -157,6 +188,16 @@ unsigned Analog_Value_Instance_To_Index(uint32_t object_instance)
return index;
}
/**
* This function is used to detect a value change,
* using the new value compared against the prior
* value, using a delta as threshold.
*
* This method will update the COV-changed attribute.
*
* @param index Object index
* @param value Given present value.
*/
static void Analog_Value_COV_Detect(unsigned int index, float value)
{
float prior_value = 0.0;
@@ -203,6 +244,13 @@ bool Analog_Value_Present_Value_Set(
return status;
}
/**
* For a given object instance-number, return the present value.
*
* @param object_instance - object-instance number of the object
*
* @return Present value
*/
float Analog_Value_Present_Value(uint32_t object_instance)
{
float value = 0;
@@ -216,7 +264,16 @@ float Analog_Value_Present_Value(uint32_t object_instance)
return value;
}
/* note: the object name must be unique within this device */
/**
* For a given object instance-number, return the name.
*
* Note: the object name must be unique within this device
*
* @param object_instance - object-instance number of the object
* @param object_name - object name/string pointer
*
* @return true/false
*/
bool Analog_Value_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
@@ -370,7 +427,13 @@ void Analog_Value_Out_Of_Service_Set(uint32_t object_instance, bool value)
}
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
/**
* Return the requested property of the analog value.
*
* @param rpdata Property requested, see for BACNET_READ_PROPERTY_DATA details.
*
* @return apdu len, or BACNET_STATUS_ERROR on error
*/
int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
@@ -386,7 +449,11 @@ int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
unsigned i = 0;
#endif
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
/* Valid data? */
if (rpdata == NULL) {
return 0;
}
if ((rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
@@ -394,12 +461,14 @@ int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
apdu = rpdata->application_data;
object_index = Analog_Value_Instance_To_Index(rpdata->object_instance);
if (object_index < MAX_ANALOG_VALUES) {
CurrentAV = &AV_Descr[object_index];
} else {
if (object_index >= MAX_ANALOG_VALUES) {
rpdata->error_class = ERROR_CLASS_OBJECT;
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
return BACNET_STATUS_ERROR;
}
CurrentAV = &AV_Descr[object_index];
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
@@ -408,9 +477,10 @@ int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
Analog_Value_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
if (Analog_Value_Object_Name(rpdata->object_instance, &char_string)) {
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
}
break;
case PROP_OBJECT_TYPE:
@@ -594,7 +664,13 @@ int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
return apdu_len;
}
/* returns true if successful */
/**
* Set the requested property of the analog value.
*
* @param wp_data Property requested, see for BACNET_WRITE_PROPERTY_DATA details.
*
* @return true if successful
*/
bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
@@ -603,6 +679,15 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
BACNET_APPLICATION_DATA_VALUE value;
ANALOG_VALUE_DESCR *CurrentAV;
/* Valid data? */
if (wp_data == NULL) {
return false;
}
if ((wp_data->application_data == NULL) ||
(wp_data->application_data_len == 0)) {
return false;
}
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
@@ -621,13 +706,17 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
/* Valid object? */
object_index = Analog_Value_Instance_To_Index(wp_data->object_instance);
if (object_index < MAX_ANALOG_VALUES) {
CurrentAV = &AV_Descr[object_index];
} else {
if (object_index >= MAX_ANALOG_VALUES) {
wp_data->error_class = ERROR_CLASS_OBJECT;
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
return false;
}
CurrentAV = &AV_Descr[object_index];
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_REAL) {
+126 -29
View File
@@ -62,6 +62,13 @@ static const int Binary_Value_Properties_Optional[] = { PROP_DESCRIPTION,
static const int Binary_Value_Properties_Proprietary[] = { -1 };
/**
* Initialize the pointers for the required, the optional and the properitary value properties.
*
* @param pRequired - Pointer to the pointer of required values.
* @param pOptional - Pointer to the pointer of optional values.
* @param pProprietary - Pointer to the pointer of properitary values.
*/
void Binary_Value_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
@@ -78,6 +85,9 @@ void Binary_Value_Property_Lists(
return;
}
/**
* Initialize the binary values.
*/
void Binary_Value_Init(void)
{
unsigned i, j;
@@ -97,9 +107,15 @@ void Binary_Value_Init(void)
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
/**
* We simply have 0-n object instances. Yours might be
* more complex, and then you need validate that the
* given instance exists.
*
* @param object_instance Object instance
*
* @return true/false
*/
bool Binary_Value_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_BINARY_VALUES) {
@@ -109,24 +125,39 @@ bool Binary_Value_Valid_Instance(uint32_t object_instance)
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
/**
* Return the count of analog values.
*
* @return Count of binary values.
*/
unsigned Binary_Value_Count(void)
{
return MAX_BINARY_VALUES;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
/**
* We simply have 0-n object instances. Yours might be
* more complex, and then you need to return the instance
* that correlates to the correct index.
*
* @param index Index
*
* @return Object instance
*/
uint32_t Binary_Value_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
/**
* We simply have 0-n object instances. Yours might be
* more complex, and then you need to return the index
* that correlates to the correct instance number
*
* @param object_instance Object instance
*
* @return Index in the object table.
*/
unsigned Binary_Value_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_BINARY_VALUES;
@@ -138,6 +169,13 @@ unsigned Binary_Value_Instance_To_Index(uint32_t object_instance)
return index;
}
/**
* For a given object instance-number, return the present value.
*
* @param object_instance - object-instance number of the object
*
* @return Present value
*/
BACNET_BINARY_PV Binary_Value_Present_Value(uint32_t object_instance)
{
BACNET_BINARY_PV value = RELINQUISH_DEFAULT;
@@ -157,7 +195,16 @@ BACNET_BINARY_PV Binary_Value_Present_Value(uint32_t object_instance)
return value;
}
/* note: the object name must be unique within this device */
/**
* For a given object instance-number, return the name.
*
* Note: the object name must be unique within this device
*
* @param object_instance - object-instance number of the object
* @param object_name - object name/string pointer
*
* @return true/false
*/
bool Binary_Value_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
@@ -173,6 +220,13 @@ bool Binary_Value_Object_Name(
return status;
}
/**
* Return the OOO value, if any.
*
* @param instance Object instance.
*
* @return true/false
*/
bool Binary_Value_Out_Of_Service(uint32_t instance)
{
unsigned index = 0;
@@ -186,6 +240,12 @@ bool Binary_Value_Out_Of_Service(uint32_t instance)
return oos_flag;
}
/**
* Set the OOO value, if any.
*
* @param instance Object instance.
* @param oos_flag New OOO value.
*/
void Binary_Value_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
{
unsigned index = 0;
@@ -196,7 +256,13 @@ void Binary_Value_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
}
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
/**
* Return the requested property of the binary value.
*
* @param rpdata Property requested, see for BACNET_READ_PROPERTY_DATA details.
*
* @return apdu len, or BACNET_STATUS_ERROR on error
*/
int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
@@ -209,10 +275,23 @@ int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
bool state = false;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
/* Valid data? */
if (rpdata == NULL) {
return 0;
}
if ((rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
/* Valid object index? */
object_index = Binary_Value_Instance_To_Index(rpdata->object_instance);
if (object_index >= MAX_BINARY_VALUES) {
rpdata->error_class = ERROR_CLASS_OBJECT;
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
return BACNET_STATUS_ERROR;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
@@ -223,9 +302,10 @@ int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
You could make Description writable and different */
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
Binary_Value_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
if (Binary_Value_Object_Name(rpdata->object_instance, &char_string)) {
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
}
break;
case PROP_OBJECT_TYPE:
apdu_len =
@@ -263,8 +343,6 @@ int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
*/
/* into one packet. */
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
object_index =
Binary_Value_Instance_To_Index(rpdata->object_instance);
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
/* FIXME: check if we have room before adding it to APDU */
if (Binary_Value_Level[object_index][i] == BINARY_NULL) {
@@ -285,8 +363,6 @@ int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
}
}
} else {
object_index =
Binary_Value_Instance_To_Index(rpdata->object_instance);
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
if (Binary_Value_Level[object_index][rpdata->array_index] ==
BINARY_NULL) {
@@ -314,7 +390,8 @@ int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
/* Only array properties can have array options. */
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
@@ -325,7 +402,13 @@ int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
return apdu_len;
}
/* returns true if successful */
/**
* Set the requested property of the binary value.
*
* @param wp_data Property requested, see for BACNET_WRITE_PROPERTY_DATA details.
*
* @return true if successful
*/
bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
@@ -335,7 +418,16 @@ bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
/* decode the some of the request */
/* Valid data? */
if (wp_data == NULL) {
return false;
}
if ((wp_data->application_data == NULL) || \
(wp_data->application_data_len == 0)) {
return false;
}
/* Decode the some of the request. */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
@@ -345,13 +437,22 @@ bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* only array properties can have array options */
/* Only array properties can have array options. */
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
/* Valid object index? */
object_index = Binary_Value_Instance_To_Index(wp_data->object_instance);
if (object_index >= MAX_BINARY_VALUES) {
wp_data->error_class = ERROR_CLASS_OBJECT;
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
@@ -363,8 +464,6 @@ bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
(priority != 6 /* reserved */) &&
(value.type.Enumerated <= MAX_BINARY_PV)) {
level = (BACNET_BINARY_PV)value.type.Enumerated;
object_index = Binary_Value_Instance_To_Index(
wp_data->object_instance);
priority--;
Binary_Value_Level[object_index][priority] = level;
/* Note: you could set the physical output here if we
@@ -389,8 +488,6 @@ bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
&wp_data->error_class, &wp_data->error_code);
if (status) {
level = BINARY_NULL;
object_index = Binary_Value_Instance_To_Index(
wp_data->object_instance);
priority = wp_data->priority;
if (priority && (priority <= BACNET_MAX_PRIORITY)) {
priority--;
+172 -33
View File
@@ -28,6 +28,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
@@ -60,6 +61,13 @@ static const int Properties_Optional[] = { PROP_EVENT_STATE,
static const int Properties_Proprietary[] = { -1 };
/**
* Initialize the pointers for the required, the optional and the properitary value properties.
*
* @param pRequired - Pointer to the pointer of required values.
* @param pOptional - Pointer to the pointer of optional values.
* @param pProprietary - Pointer to the pointer of properitary values.
*/
void CharacterString_Value_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
@@ -76,6 +84,9 @@ void CharacterString_Value_Property_Lists(
return;
}
/**
* Initialize the character string values.
*/
void CharacterString_Value_Init(void)
{
unsigned i;
@@ -92,9 +103,15 @@ void CharacterString_Value_Init(void)
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
/**
* We simply have 0-n object instances. Yours might be
* more complex, and then you need to return the instance
* that correlates to the correct index.
*
* @param object_instance Object instance
*
* @return Object instance
*/
unsigned CharacterString_Value_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_CHARACTERSTRING_VALUES;
@@ -106,21 +123,39 @@ unsigned CharacterString_Value_Instance_To_Index(uint32_t object_instance)
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
/**
* We simply have 0-n object instances. Yours might be
* more complex, and then you need to return the index
* that correlates to the correct instance number
*
* @param index Object index
*
* @return Index in the object table.
*/
uint32_t CharacterString_Value_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
/**
* Return the count of character string values.
*
* @return Count of character string values.
*/
unsigned CharacterString_Value_Count(void)
{
return MAX_CHARACTERSTRING_VALUES;
}
/**
* We simply have 0-n object instances. Yours might be
* more complex, and then you need validate that the
* given instance exists.
*
* @param object_instance Object instance
*
* @return true/false
*/
bool CharacterString_Value_Valid_Instance(uint32_t object_instance)
{
unsigned index = 0; /* offset from instance lookup */
@@ -133,6 +168,16 @@ bool CharacterString_Value_Valid_Instance(uint32_t object_instance)
return false;
}
/**
* For a given object instance-number, read the present-value.
*
* @param object_instance - object-instance number of the object
* @param object_name - Pointer to the new BACnet string value,
* taking the value.
*
* @return true if values are within range and present-value
* is returned.
*/
bool CharacterString_Value_Present_Value(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
@@ -147,6 +192,15 @@ bool CharacterString_Value_Present_Value(
return status;
}
/**
* For a given object instance-number, sets the present-value,
* taken from another BACnet string.
*
* @param object_instance - object-instance number of the object
* @param object_name - Pointer to the new BACnet string value.
*
* @return true if values are within range and present-value is set.
*/
bool CharacterString_Value_Present_Value_Set(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
@@ -161,6 +215,13 @@ bool CharacterString_Value_Present_Value_Set(
return status;
}
/**
* For a given object instance-number, read the out of service value.
*
* @param object_instance - object-instance number of the object
*
* @return true/false
*/
bool CharacterString_Value_Out_Of_Service(uint32_t object_instance)
{
bool value = false;
@@ -174,6 +235,12 @@ bool CharacterString_Value_Out_Of_Service(uint32_t object_instance)
return value;
}
/**
* For a given object instance-number, set the out of service value.
*
* @param object_instance - object-instance number of the object
* @param true/false
*/
static void CharacterString_Value_Out_Of_Service_Set(
uint32_t object_instance, bool value)
{
@@ -187,6 +254,13 @@ static void CharacterString_Value_Out_Of_Service_Set(
return;
}
/**
* For a given object instance-number, return the description.
*
* @param object_instance - object-instance number of the object
*
* @return C-string pointer to the description.
*/
static char *CharacterString_Value_Description(uint32_t object_instance)
{
unsigned index = 0; /* offset from instance lookup */
@@ -200,8 +274,16 @@ static char *CharacterString_Value_Description(uint32_t object_instance)
return pName;
}
/**
* For a given object instance-number, set the description text.
*
* @param object_instance - object-instance number of the object
* @param new_descr - C-String pointer to the string, representing the description text
*
* @return True on success, false otherwise.
*/
bool CharacterString_Value_Description_Set(
uint32_t object_instance, char *new_name)
uint32_t object_instance, char *new_descr)
{
unsigned index = 0; /* offset from instance lookup */
size_t i = 0; /* loop counter */
@@ -210,23 +292,29 @@ bool CharacterString_Value_Description_Set(
index = CharacterString_Value_Instance_To_Index(object_instance);
if (index < MAX_CHARACTERSTRING_VALUES) {
status = true;
if (new_name) {
if (new_descr) {
for (i = 0; i < sizeof(Object_Description[index]); i++) {
Object_Description[index][i] = new_name[i];
if (new_name[i] == 0) {
Object_Description[index][i] = new_descr[i];
if (new_descr[i] == 0) {
break;
}
}
} else {
for (i = 0; i < sizeof(Object_Description[index]); i++) {
Object_Description[index][i] = 0;
}
memset(&Object_Description[index][0], 0, sizeof(Object_Description[index]));
}
}
return status;
}
/**
* For a given object instance-number, return the object text.
*
* @param object_instance - object-instance number of the object
* @param object_name - Pointer to the BACnet string object that shall take the object name
*
* @return True on success, false otherwise.
*/
bool CharacterString_Value_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
@@ -241,7 +329,16 @@ bool CharacterString_Value_Object_Name(
return status;
}
/* note: the object name must be unique within this device */
/**
* For a given object instance-number, set the object text.
*
* Note: the object name must be unique within this device
*
* @param object_instance - object-instance number of the object
* @param new_name - C-String pointer to the text, representing the object name
*
* @return True on success, false otherwise.
*/
bool CharacterString_Value_Name_Set(uint32_t object_instance, char *new_name)
{
unsigned index = 0; /* offset from instance lookup */
@@ -260,16 +357,20 @@ bool CharacterString_Value_Name_Set(uint32_t object_instance, char *new_name)
}
}
} else {
for (i = 0; i < sizeof(Object_Name[index]); i++) {
Object_Name[index][i] = 0;
}
memset(&Object_Name[index][0], 0, sizeof(Object_Name[index]));
}
}
return status;
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
/**
* Return the requested property of the character string value.
*
* @param rpdata Property requested, see for BACNET_READ_PROPERTY_DATA details.
*
* @return apdu len, or BACNET_STATUS_ERROR on error
*/
int CharacterString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
@@ -279,11 +380,25 @@ int CharacterString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
bool state = false;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
/* Valid data? */
if (rpdata == NULL) {
return 0;
}
if ((rpdata->application_data == NULL) || \
(rpdata->application_data_len == 0)) {
return 0;
}
/* Valid object? */
object_index = CharacterString_Value_Instance_To_Index(rpdata->object_instance);
if (object_index >= MAX_CHARACTERSTRING_VALUES) {
rpdata->error_class = ERROR_CLASS_OBJECT;
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
return BACNET_STATUS_ERROR;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(&apdu[0],
@@ -292,16 +407,18 @@ int CharacterString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
/* note: Name and Description don't have to be the same.
You could make Description writable and different */
case PROP_OBJECT_NAME:
CharacterString_Value_Object_Name(
rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
if (CharacterString_Value_Object_Name(
rpdata->object_instance, &char_string)) {
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
}
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(&char_string,
CharacterString_Value_Description(rpdata->object_instance));
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
if (characterstring_init_ansi(&char_string,
CharacterString_Value_Description(rpdata->object_instance))) {
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
}
break;
case PROP_OBJECT_TYPE:
apdu_len = encode_application_enumerated(
@@ -334,8 +451,6 @@ int CharacterString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
object_index = CharacterString_Value_Instance_To_Index(
rpdata->object_instance);
state = Out_Of_Service[object_index];
apdu_len = encode_application_boolean(&apdu[0], state);
break;
@@ -356,14 +471,29 @@ int CharacterString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
return apdu_len;
}
/* returns true if successful */
/**
* Set the requested property of the character string value.
*
* @param wp_data Property requested, see for BACNET_WRITE_PROPERTY_DATA details.
*
* @return true if successful
*/
bool CharacterString_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
unsigned object_index = 0;
BACNET_APPLICATION_DATA_VALUE value;
/* decode the some of the request */
if (wp_data == NULL) {
return false;
}
if ((wp_data->application_data == NULL) || \
(wp_data->application_data_len == 0)) {
return false;
}
/* Decode the some of the request. */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
@@ -373,6 +503,15 @@ bool CharacterString_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* Valid object? */
object_index = CharacterString_Value_Instance_To_Index(wp_data->object_instance);
if (object_index >= MAX_CHARACTERSTRING_VALUES) {
wp_data->error_class = ERROR_CLASS_OBJECT;
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
status = WPValidateArgType(&value,
+228 -142
View File
@@ -94,6 +94,12 @@ static BACNET_SERVICES_SUPPORTED
/* If they are not set, they are handled by a reject message */
static confirmed_function Confirmed_Function[MAX_BACNET_CONFIRMED_SERVICE];
/**
* @brief Set a handler function for the given confirmed service.
*
* @param service_choice Service, see SERVICE_CONFIRMED_X enumeration.
* @param pFunction Pointer to the function, being in charge of the service.
*/
void apdu_set_confirmed_handler(
BACNET_CONFIRMED_SERVICE service_choice, confirmed_function pFunction)
{
@@ -105,6 +111,12 @@ void apdu_set_confirmed_handler(
/* Allow the APDU handler to automatically reject */
static confirmed_function Unrecognized_Service_Handler;
/**
* @brief Set a handler function called for an unsupported service.
*
* @param pFunction Pointer to the function, being in charge,
* if a unsupported service has been requested.
*/
void apdu_set_unrecognized_service_handler_handler(confirmed_function pFunction)
{
Unrecognized_Service_Handler = pFunction;
@@ -115,6 +127,12 @@ void apdu_set_unrecognized_service_handler_handler(confirmed_function pFunction)
static unconfirmed_function
Unconfirmed_Function[MAX_BACNET_UNCONFIRMED_SERVICE];
/**
* @brief Set a handler function for the given unconfirmed service.
*
* @param service_choice Service, see SERVICE_UNCONFIRMED_X enumeration.
* @param pFunction Pointer to the function, being in charge of the service.
*/
void apdu_set_unconfirmed_handler(
BACNET_UNCONFIRMED_SERVICE service_choice, unconfirmed_function pFunction)
{
@@ -123,6 +141,13 @@ void apdu_set_unconfirmed_handler(
}
}
/**
* @brief Checks if the given service is supported or not.
*
* @param service_supported Service in question
*
* @return true/false
*/
bool apdu_service_supported(BACNET_SERVICES_SUPPORTED service_supported)
{
int i = 0;
@@ -278,6 +303,12 @@ void apdu_set_confirmed_ack_handler(
static error_function Error_Function[MAX_BACNET_CONFIRMED_SERVICE];
/**
* @brief Set a error handler function for the given confirmed service.
*
* @param service_choice Service, see SERVICE_CONFIRMED_X enumeration.
* @param pFunction Pointer to the function, being in charge of the error handling.
*/
void apdu_set_error_handler(
BACNET_CONFIRMED_SERVICE service_choice, error_function pFunction)
{
@@ -295,11 +326,35 @@ void apdu_set_abort_handler(abort_function pFunction)
static reject_function Reject_Function;
/**
* @brief Set a handler function called for a rejected service.
*
* @param pFunction Pointer to the function, being in charge,
* if a service request will be rejected.
*/
void apdu_set_reject_handler(reject_function pFunction)
{
Reject_Function = pFunction;
}
/**
* @brief Decode the given confirmed service request from the received data.
*
* @param apdu Received data buffer.
* @param apdu_len Count of valid bytes in the receive buffer.
* @param service_data Pointer of the structure being filled with the service
* data.
* @param service_choice Pointer to a variable taking the service requested,
* see SERVICE_CONFIRMED_X
* @param service_request Pointer to a pointer that will take the pointer
* were the service request data can be found in
* the receive buffer.
* @param service_request_len Pointer to a variable that takes the length
* of the service request data.
*
* @return The length of the service data and reflects the offset, where
* we are in PDU after the service had been decoded.
*/
uint16_t apdu_decode_confirmed_service_request(uint8_t *apdu, /* APDU data */
uint16_t apdu_len,
BACNET_CONFIRMED_SERVICE_DATA *service_data,
@@ -382,13 +437,19 @@ static bool apdu_confirmed_dcc_disabled(uint8_t service_choice)
return status;
}
/* When network communications are completely disabled,
only DeviceCommunicationControl and ReinitializeDevice APDUs
shall be processed and no messages shall be initiated. */
/* If the request is valid and the 'Enable/Disable' parameter is
DISABLE_INITIATION, the responding BACnet-user shall
discontinue the initiation of messages except for I-Am
requests issued in accordance with the Who-Is service procedure.*/
/**
* When network communications are completely disabled,
* only DeviceCommunicationControl and ReinitializeDevice APDUs
* shall be processed and no messages shall be initiated.
* If the request is valid and the 'Enable/Disable' parameter is
* DISABLE_INITIATION, the responding BACnet-user shall
* discontinue the initiation of messages except for I-Am
* requests issued in accordance with the Who-Is service procedure.
*
* @param service_choice Service, like SERVICE_UNCONFIRMED_WHO_IS
*
* @return true, if being disabled.
*/
static bool apdu_unconfirmed_dcc_disabled(uint8_t service_choice)
{
bool status = false;
@@ -462,101 +523,107 @@ void apdu_handler(BACNET_ADDRESS *src,
}
break;
case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST:
service_choice = apdu[1];
service_request = &apdu[2];
service_request_len = apdu_len - 2;
if (apdu_unconfirmed_dcc_disabled(service_choice)) {
/* When network communications are disabled,
only DeviceCommunicationControl and ReinitializeDevice
APDUs shall be processed and no messages shall be
initiated. If communications have been initiation
disabled, then WhoIs may be processed. */
break;
}
if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) {
if (Unconfirmed_Function[service_choice]) {
Unconfirmed_Function[service_choice](
service_request, service_request_len, src);
if (apdu_len >= 2) {
service_choice = apdu[1];
service_request = &apdu[2];
service_request_len = apdu_len - 2;
if (apdu_unconfirmed_dcc_disabled(service_choice)) {
/* When network communications are disabled,
only DeviceCommunicationControl and ReinitializeDevice
APDUs shall be processed and no messages shall be
initiated. If communications have been initiation
disabled, then WhoIs may be processed. */
break;
}
if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) {
if (Unconfirmed_Function[service_choice]) {
Unconfirmed_Function[service_choice](
service_request, service_request_len, src);
}
}
}
break;
case PDU_TYPE_SIMPLE_ACK:
invoke_id = apdu[1];
service_choice = apdu[2];
switch (service_choice) {
case SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM:
case SERVICE_CONFIRMED_COV_NOTIFICATION:
case SERVICE_CONFIRMED_EVENT_NOTIFICATION:
case SERVICE_CONFIRMED_SUBSCRIBE_COV:
case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY:
case SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION:
/* Object Access Services */
case SERVICE_CONFIRMED_ADD_LIST_ELEMENT:
case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT:
case SERVICE_CONFIRMED_DELETE_OBJECT:
case SERVICE_CONFIRMED_WRITE_PROPERTY:
case SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE:
/* Remote Device Management Services */
case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL:
case SERVICE_CONFIRMED_REINITIALIZE_DEVICE:
case SERVICE_CONFIRMED_TEXT_MESSAGE:
/* Virtual Terminal Services */
case SERVICE_CONFIRMED_VT_CLOSE:
/* Security Services */
case SERVICE_CONFIRMED_REQUEST_KEY:
if (Confirmed_ACK_Function[service_choice] != NULL) {
((confirmed_simple_ack_function)
Confirmed_ACK_Function[service_choice])(
src, invoke_id);
}
tsm_free_invoke_id(invoke_id);
break;
default:
break;
if (apdu_len >= 3) {
invoke_id = apdu[1];
service_choice = apdu[2];
switch (service_choice) {
case SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM:
case SERVICE_CONFIRMED_COV_NOTIFICATION:
case SERVICE_CONFIRMED_EVENT_NOTIFICATION:
case SERVICE_CONFIRMED_SUBSCRIBE_COV:
case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY:
case SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION:
/* Object Access Services */
case SERVICE_CONFIRMED_ADD_LIST_ELEMENT:
case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT:
case SERVICE_CONFIRMED_DELETE_OBJECT:
case SERVICE_CONFIRMED_WRITE_PROPERTY:
case SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE:
/* Remote Device Management Services */
case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL:
case SERVICE_CONFIRMED_REINITIALIZE_DEVICE:
case SERVICE_CONFIRMED_TEXT_MESSAGE:
/* Virtual Terminal Services */
case SERVICE_CONFIRMED_VT_CLOSE:
/* Security Services */
case SERVICE_CONFIRMED_REQUEST_KEY:
if (Confirmed_ACK_Function[service_choice] != NULL) {
((confirmed_simple_ack_function)
Confirmed_ACK_Function[service_choice])(
src, invoke_id);
}
tsm_free_invoke_id(invoke_id);
break;
default:
break;
}
}
break;
case PDU_TYPE_COMPLEX_ACK:
service_ack_data.segmented_message =
(apdu[0] & BIT(3)) ? true : false;
service_ack_data.more_follows =
(apdu[0] & BIT(2)) ? true : false;
invoke_id = service_ack_data.invoke_id = apdu[1];
len = 2;
if (service_ack_data.segmented_message) {
service_ack_data.sequence_number = apdu[len++];
service_ack_data.proposed_window_number = apdu[len++];
}
service_choice = apdu[len++];
service_request = &apdu[len];
service_request_len = apdu_len - (uint16_t)len;
switch (service_choice) {
case SERVICE_CONFIRMED_GET_ALARM_SUMMARY:
case SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY:
case SERVICE_CONFIRMED_GET_EVENT_INFORMATION:
/* File Access Services */
case SERVICE_CONFIRMED_ATOMIC_READ_FILE:
case SERVICE_CONFIRMED_ATOMIC_WRITE_FILE:
/* Object Access Services */
case SERVICE_CONFIRMED_CREATE_OBJECT:
case SERVICE_CONFIRMED_READ_PROPERTY:
case SERVICE_CONFIRMED_READ_PROP_CONDITIONAL:
case SERVICE_CONFIRMED_READ_PROP_MULTIPLE:
case SERVICE_CONFIRMED_READ_RANGE:
case SERVICE_CONFIRMED_PRIVATE_TRANSFER:
/* Virtual Terminal Services */
case SERVICE_CONFIRMED_VT_OPEN:
case SERVICE_CONFIRMED_VT_DATA:
/* Security Services */
case SERVICE_CONFIRMED_AUTHENTICATE:
if (Confirmed_ACK_Function[service_choice] != NULL) {
(Confirmed_ACK_Function[service_choice])(
service_request, service_request_len, src,
&service_ack_data);
}
tsm_free_invoke_id(invoke_id);
break;
default:
break;
if (apdu_len >= 3) {
service_ack_data.segmented_message =
(apdu[0] & BIT(3)) ? true : false;
service_ack_data.more_follows =
(apdu[0] & BIT(2)) ? true : false;
invoke_id = service_ack_data.invoke_id = apdu[1];
len = 2;
if (service_ack_data.segmented_message) {
service_ack_data.sequence_number = apdu[len++];
service_ack_data.proposed_window_number = apdu[len++];
}
service_choice = apdu[len++];
service_request = &apdu[len];
service_request_len = apdu_len - (uint16_t)len;
switch (service_choice) {
case SERVICE_CONFIRMED_GET_ALARM_SUMMARY:
case SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY:
case SERVICE_CONFIRMED_GET_EVENT_INFORMATION:
/* File Access Services */
case SERVICE_CONFIRMED_ATOMIC_READ_FILE:
case SERVICE_CONFIRMED_ATOMIC_WRITE_FILE:
/* Object Access Services */
case SERVICE_CONFIRMED_CREATE_OBJECT:
case SERVICE_CONFIRMED_READ_PROPERTY:
case SERVICE_CONFIRMED_READ_PROP_CONDITIONAL:
case SERVICE_CONFIRMED_READ_PROP_MULTIPLE:
case SERVICE_CONFIRMED_READ_RANGE:
case SERVICE_CONFIRMED_PRIVATE_TRANSFER:
/* Virtual Terminal Services */
case SERVICE_CONFIRMED_VT_OPEN:
case SERVICE_CONFIRMED_VT_DATA:
/* Security Services */
case SERVICE_CONFIRMED_AUTHENTICATE:
if (Confirmed_ACK_Function[service_choice] != NULL) {
(Confirmed_ACK_Function[service_choice])(
service_request, service_request_len, src,
&service_ack_data);
}
tsm_free_invoke_id(invoke_id);
break;
default:
break;
}
}
break;
case PDU_TYPE_SEGMENT_ACK:
@@ -565,66 +632,85 @@ void apdu_handler(BACNET_ADDRESS *src,
tsm_free_invoke_id(invoke_id);
break;
case PDU_TYPE_ERROR:
invoke_id = apdu[1];
service_choice = apdu[2];
len = 3;
if (apdu_len >= 3) {
invoke_id = apdu[1];
service_choice = apdu[2];
len = 3;
/* FIXME: Currently special case for C_P_T but there are others
which may need consideration such as ChangeList-Error,
CreateObject-Error, WritePropertyMultiple-Error and
VTClose_Error but they may be left as is for now until
support for these services is added */
/* FIXME: Currently special case for C_P_T but there are others
which may need consideration such as ChangeList-Error,
CreateObject-Error, WritePropertyMultiple-Error and
VTClose_Error but they may be left as is for now until
support for these services is added */
if (service_choice ==
SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over
opening tag 0 */
if (decode_is_opening_tag_number(&apdu[len], 0)) {
len++; /* a tag number of 0 is not extended so only one
octet */
if (service_choice ==
SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over
opening tag 0 */
if (decode_is_opening_tag_number(&apdu[len], 0)) {
len++; /* a tag number of 0 is not extended so only one
octet */
}
}
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
/* FIXME: we could validate that the tag is enumerated... */
len += decode_enumerated(&apdu[len], len_value, &error_class);
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
/* FIXME: we could validate that the tag is enumerated... */
len += decode_enumerated(&apdu[len], len_value, &error_code);
if (service_choice ==
SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over
closing tag 0 */
if (decode_is_closing_tag_number(&apdu[len], 0)) {
len++; /* a tag number of 0 is not extended so only one
octet */
if (len < apdu_len) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
if (len < apdu_len) {
/* FIXME: we could validate that the tag is enumerated... */
len += decode_enumerated(&apdu[len], len_value, &error_class);
if (len < apdu_len) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
if (len < apdu_len) {
/* FIXME: we could validate that the tag is enumerated... */
len += decode_enumerated(&apdu[len], len_value, &error_code);
if (service_choice ==
SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over
closing tag 0 */
if (decode_is_closing_tag_number(&apdu[len], 0)) {
len++; /* a tag number of 0 is not extended so only one
octet */
}
}
}
}
}
}
}
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
if (Error_Function[service_choice]) {
Error_Function[service_choice](src, invoke_id,
(BACNET_ERROR_CLASS)error_class,
(BACNET_ERROR_CODE)error_code);
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
if (Error_Function[service_choice]) {
Error_Function[service_choice](src, invoke_id,
(BACNET_ERROR_CLASS)error_class,
(BACNET_ERROR_CODE)error_code);
}
}
tsm_free_invoke_id(invoke_id);
}
tsm_free_invoke_id(invoke_id);
break;
case PDU_TYPE_REJECT:
invoke_id = apdu[1];
reason = apdu[2];
if (Reject_Function) {
Reject_Function(src, invoke_id, reason);
if (apdu_len >= 3) {
invoke_id = apdu[1];
reason = apdu[2];
if (Reject_Function) {
Reject_Function(src, invoke_id, reason);
}
tsm_free_invoke_id(invoke_id);
}
tsm_free_invoke_id(invoke_id);
break;
case PDU_TYPE_ABORT:
server = apdu[0] & 0x01;
invoke_id = apdu[1];
reason = apdu[2];
if (Abort_Function) {
Abort_Function(src, invoke_id, reason, server);
if (apdu_len >= 3) {
server = apdu[0] & 0x01;
invoke_id = apdu[1];
reason = apdu[2];
if (Abort_Function) {
Abort_Function(src, invoke_id, reason, server);
}
tsm_free_invoke_id(invoke_id);
}
tsm_free_invoke_id(invoke_id);
break;
default:
break;
+27 -26
View File
@@ -802,36 +802,37 @@ void handler_cov_subscribe(uint8_t *service_request,
fprintf(stderr, "SubscribeCOV: Segmented message. Sending Abort!\n");
#endif
error = true;
goto COV_ABORT;
}
len = cov_subscribe_decode_service_request(
service_request, service_len, &cov_data);
#if PRINT_ENABLED
if (len <= 0)
fprintf(stderr, "SubscribeCOV: Unable to decode Request!\n");
#endif
if (len < 0) {
error = true;
goto COV_ABORT;
}
cov_data.error_class = ERROR_CLASS_OBJECT;
cov_data.error_code = ERROR_CODE_UNKNOWN_OBJECT;
success = cov_subscribe(
src, &cov_data, &cov_data.error_class, &cov_data.error_code);
if (success) {
apdu_len = encode_simple_ack(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_SUBSCRIBE_COV);
#if PRINT_ENABLED
fprintf(stderr, "SubscribeCOV: Sending Simple Ack!\n");
#endif
} else {
len = BACNET_STATUS_ERROR;
error = true;
len = cov_subscribe_decode_service_request(
service_request, service_len, &cov_data);
#if PRINT_ENABLED
fprintf(stderr, "SubscribeCOV: Sending Error!\n");
if (len <= 0)
fprintf(stderr, "SubscribeCOV: Unable to decode Request!\n");
#endif
if (len < 0) {
error = true;
} else {
cov_data.error_class = ERROR_CLASS_OBJECT;
cov_data.error_code = ERROR_CODE_UNKNOWN_OBJECT;
success = cov_subscribe(
src, &cov_data, &cov_data.error_class, &cov_data.error_code);
if (success) {
apdu_len = encode_simple_ack(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_SUBSCRIBE_COV);
#if PRINT_ENABLED
fprintf(stderr, "SubscribeCOV: Sending Simple Ack!\n");
#endif
} else {
len = BACNET_STATUS_ERROR;
error = true;
#if PRINT_ENABLED
fprintf(stderr, "SubscribeCOV: Sending Error!\n");
#endif
}
}
}
COV_ABORT:
/* Error? */
if (error) {
if (len == BACNET_STATUS_ABORT) {
apdu_len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
+53 -36
View File
@@ -68,6 +68,7 @@ void handler_write_property(uint8_t *service_request,
{
BACNET_WRITE_PROPERTY_DATA wp_data;
int len = 0;
bool bcontinue = true;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
int bytes_sent = 0;
@@ -88,47 +89,55 @@ void handler_write_property(uint8_t *service_request,
#if PRINT_ENABLED
fprintf(stderr, "WP: Segmented message. Sending Abort!\n");
#endif
goto WP_ABORT;
} /* decode the service request only */
len = wp_decode_service_request(service_request, service_len, &wp_data);
#if PRINT_ENABLED
if (len > 0)
fprintf(stderr,
"WP: type=%lu instance=%lu property=%lu priority=%lu index=%ld\n",
(unsigned long)wp_data.object_type,
(unsigned long)wp_data.object_instance,
(unsigned long)wp_data.object_property,
(unsigned long)wp_data.priority, (long)wp_data.array_index);
else
fprintf(stderr, "WP: Unable to decode Request!\n");
#endif
/* bad decoding or something we didn't understand - send an abort */
if (len <= 0) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr, "WP: Bad Encoding. Sending Abort!\n");
#endif
goto WP_ABORT;
bcontinue = false;
}
if (Device_Write_Property(&wp_data)) {
len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY);
if (bcontinue) {
/* decode the service request only */
len = wp_decode_service_request(service_request, service_len, &wp_data);
#if PRINT_ENABLED
fprintf(stderr, "WP: Sending Simple Ack!\n");
if (len > 0)
fprintf(stderr,
"WP: type=%lu instance=%lu property=%lu priority=%lu index=%ld\n",
(unsigned long)wp_data.object_type,
(unsigned long)wp_data.object_instance,
(unsigned long)wp_data.object_property,
(unsigned long)wp_data.priority, (long)wp_data.array_index);
else
fprintf(stderr, "WP: Unable to decode Request!\n");
#endif
} else {
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY,
wp_data.error_class, wp_data.error_code);
/* bad decoding or something we didn't understand - send an abort */
if (len <= 0) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr, "WP: Sending Error!\n");
fprintf(stderr, "WP: Bad Encoding. Sending Abort!\n");
#endif
bcontinue = false;
}
if (bcontinue) {
if (Device_Write_Property(&wp_data)) {
len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY);
#if PRINT_ENABLED
fprintf(stderr, "WP: Sending Simple Ack!\n");
#endif
} else {
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY,
wp_data.error_class, wp_data.error_code);
#if PRINT_ENABLED
fprintf(stderr, "WP: Sending Error!\n");
#endif
}
}
}
WP_ABORT:
/* Send PDU */
pdu_len += len;
bytes_sent = datalink_send_pdu(
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
bytes_sent = datalink_send_pdu( \
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "WP: Failed to send PDU (%s)!\n", strerror(errno));
@@ -138,12 +147,20 @@ WP_ABORT:
return;
}
/** Perform basic validation of Write Property argument based on
/**
* @brief Perform basic validation of Write Property argument based on
* the assumption that it is a string. Check for correct data type,
* correct encoding (fixed here as ANSI X34),correct length, and
* finally if it is allowed to be empty.
*
* @param pValue Pointer to the application data value representing the string.
* @param iMaxLen Maximum string length allowed.
* @param bEmptyAllowed true, if empty strings shall be allowed.
* @param pErrorClass Pointer to a variable taking the error class.
* @param pErrorCode Pointer to a variable taking the error code.
*
* @return True on success, false otherwise.
*/
bool WPValidateString(BACNET_APPLICATION_DATA_VALUE *pValue,
int iMaxLen,
bool bEmptyAllowed,
+22 -1
View File
@@ -41,7 +41,17 @@
/** @file iam.c Encode/Decode I-Am service */
/* encode I-Am service */
/**
* @brief Encode the I-Am service.
*
* @param apdu Transmit buffer
* @param device_id Device Id
* @param max_apdu Transmit buffer size.
* @param segmentation True, if segmentation shall be featured.
* @param vendor_id Vendor Id
*
* @return Total length of the apdu, zero otherwise.
*/
int iam_encode_apdu(uint8_t *apdu,
uint32_t device_id,
unsigned max_apdu,
@@ -70,6 +80,17 @@ int iam_encode_apdu(uint8_t *apdu,
return apdu_len;
}
/**
* @brief Decode the I-Am service.
*
* @param apdu Receive buffer
* @param pDevice_id Pointer to the variable that shal ltake the device Id.
* @param pMax_apdu Pointer to a variable that shall take the decoded length.
* @param pSegmentation Pointer to a variable taking if segmentation is used.
* @param pVendor_id Pointer to a variable taking the vendor id.
*
* @return Total length of the apdu, zero otherwise.
*/
int iam_decode_service_request(uint8_t *apdu,
uint32_t *pDevice_id,
unsigned *pMax_apdu,