diff --git a/bacnet-stack/demo/handler/h_rpm.c b/bacnet-stack/demo/handler/h_rpm.c new file mode 100644 index 00000000..8bdc0da5 --- /dev/null +++ b/bacnet-stack/demo/handler/h_rpm.c @@ -0,0 +1,400 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* Enhanced by John Stachler for ReadPropertyMultiple +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "rpm.h" +#include "handlers.h" +#include "device.h" +#include "ai.h" +#include "ao.h" +#include "bi.h" + +static uint8_t Temp_Buf[MAX_APDU] = { 0 }; + +/* copy encoded apdu data to tx buffer + this is needed to make sure we do not go past our apdu buffer size */ +bool copy_apdu_data_buffer(int *apdu_len, uint8 len, uint8 npdu_len) +{ + bool copy_status = false; + + if ((*apdu_len + len) <= MAX_APDU) { + memcpy(&Handler_Transmit_Buffer[*apdu_len + npdu_len], &Temp_Buf[0], len); + *apdu_len += len; + copy_status = true; + } + + return copy_status; +} + +void handler_read_property_multiple(uint8_t * service_request, uint16_t service_len, BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + int len = 0; + int decode_len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + bool error = false; + bool done; + bool property_found; + int bytes_sent; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + BACNET_ADDRESS my_address; + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance = 0; + int apdu_len = 0; + int npdu_len; + BACNET_PROPERTY_ID object_property, temp_object_property; + int32_t array_index = 0; + uint8 index; + uint8 num_properties; + + /* jps_debug - see if we are utilizing all the buffer */ + /* memset(&Handler_Transmit_Buffer[0], 0xff, MAX_MPDU);*/ + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + npdu_len = npdu_encode_pdu( + &Handler_Transmit_Buffer[0], + src, &my_address, &npdu_data); +#if PRINT_ENABLED + if (service_len <= 0) + printf("RPM: Unable to decode request!\r\n"); +#endif + /* bad decoding - send an abort */ + if (service_len == 0) + { + apdu_len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + printf("RPM: Sending Abort!\r\n"); +#endif + } else if (service_data->segmented_message) { + apdu_len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); +#if PRINT_ENABLED + printf("RPM: Sending Abort!\r\n"); +#endif + } else { + /* decode apdu request & encode apdu reply + encode complex ack, invoke id, service choice */ + apdu_len = rpm_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id); + do + { + len = rpm_ack_decode_object_id( + &service_request[decode_len], + service_len - decode_len, + &object_type, &object_instance); + if (len < 0) { + break; + } else { + decode_len += len; + } + len = rpm_ack_encode_apdu_object_begin( + &Temp_Buf[0], + object_type, object_instance); + if (!copy_apdu_data_buffer(&apdu_len, len, npdu_len)) { + apdu_len = abort_encode_apdu( + &Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); + break; + } + /* do each property of this object of the RPM request */ + done = true; + num_properties = 0; + property_found = false; + do + { + if (done) { + len = rpm_decode_object_property( + &service_request[decode_len], + service_len - decode_len, + &object_property, + &array_index); + if (len < 0) { + break; + } else { + index = 0; + decode_len += len; + temp_object_property = object_property; + } + } + /* handle the special properties */ + if ((object_property == PROP_ALL) || + (object_property == PROP_REQUIRED) || + (object_property == PROP_OPTIONAL)) + { + done = false; + switch(object_type) + { + case OBJECT_DEVICE: + property_found = Device_Property( + &index, &temp_object_property, + object_property); + break; + + case OBJECT_ANALOG_INPUT: + property_found = Analog_Input_Property( + &index, &temp_object_property, + object_property); + break; + + case OBJECT_BINARY_INPUT: + property_found = Binary_Input_Property( + &index, &temp_object_property, + object_property); + break; + + case OBJECT_ANALOG_OUTPUT: + property_found = Analog_Output_Property( + &index, &temp_object_property, + object_property); + break; + } + if (property_found) { + num_properties++; + } else { + if (num_properties > 0) { + done = true; + /* check for another property */ + len = rpm_decode_object_property( + &service_request[decode_len], + service_len - decode_len, + &object_property, + &array_index); + if (len < 0) { + /* check for closing tag */ + len = rpm_ack_decode_object_end( + &service_request[decode_len], + service_len - decode_len); + if (len == 1) { + decode_len++; + } + break; + } else { + decode_len += len; + temp_object_property = object_property; + property_found = true; + if ((object_property == PROP_ALL) || + (object_property == PROP_REQUIRED) || + (object_property == PROP_OPTIONAL)) + { + done = false; + index = 0; + switch(object_type) + { + case OBJECT_DEVICE: + property_found = + Device_Property(&index, + &temp_object_property, + object_property); + break; + + case OBJECT_ANALOG_INPUT: + property_found = + Analog_Input_Property(&index, + &temp_object_property, + object_property); + break; + + case OBJECT_BINARY_INPUT: + property_found = + Binary_Input_Property(&index, + &temp_object_property, + object_property); + break; + + case OBJECT_ANALOG_OUTPUT: + property_found = + Analog_Output_Property(&index, + &temp_object_property, + object_property); + break; + } + } + } + } else { + error = true; + done = true; + } + } + } else { + done = true; + property_found = true; + } + len = rpm_ack_encode_apdu_object_property( + &Temp_Buf[0], + temp_object_property, + array_index); + if (!copy_apdu_data_buffer(&apdu_len, len, npdu_len)) { + apdu_len = abort_encode_apdu( + &Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); + break; + } + len = encode_opening_tag(&Temp_Buf[0], 4); + if (!copy_apdu_data_buffer(&apdu_len, len, npdu_len)) { + apdu_len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); + break; + } + len = -1; + switch(object_type) { + case OBJECT_DEVICE: + if ((object_instance != Device_Object_Instance_Number()) && + ((object_instance != BACNET_MAX_INSTANCE) && + (temp_object_property != PROP_OBJECT_IDENTIFIER))) { + error_class = ERROR_CLASS_OBJECT; + error_code = ERROR_CODE_UNKNOWN_OBJECT; + } else if (!property_found) { + error_class = ERROR_CLASS_PROPERTY; + error_code = ERROR_CODE_UNKNOWN_PROPERTY; + } else { + len = Device_Encode_Property_APDU( + &Temp_Buf[0], + temp_object_property, + array_index, + &error_class, &error_code); + } + break; + case OBJECT_ANALOG_INPUT: + if (!Analog_Input_Valid_Instance(object_instance)) { + error_class = ERROR_CLASS_OBJECT; + error_code = ERROR_CODE_UNKNOWN_OBJECT; + } else if (!property_found) { + error_class = ERROR_CLASS_PROPERTY; + error_code = ERROR_CODE_UNKNOWN_PROPERTY; + } else { + len = Analog_Input_Encode_Property_APDU( + &Temp_Buf[0], + object_instance, + temp_object_property, + array_index, + &error_class, &error_code); + } + break; + case OBJECT_BINARY_INPUT: + if (!Binary_Input_Valid_Instance(object_instance)) { + error_class = ERROR_CLASS_OBJECT; + error_code = ERROR_CODE_UNKNOWN_OBJECT; + } else if (!property_found) { + error_class = ERROR_CLASS_PROPERTY; + error_code = ERROR_CODE_UNKNOWN_PROPERTY; + } else { + len = Binary_Input_Encode_Property_APDU( + &Temp_Buf[0], + object_instance, + temp_object_property, + array_index, + &error_class, &error_code); + } + break; + case OBJECT_ANALOG_OUTPUT: + if (!Analog_Output_Valid_Instance(object_instance)) { + error_class = ERROR_CLASS_OBJECT; + error_code = ERROR_CODE_UNKNOWN_OBJECT; + } else if (!property_found) { + error_class = ERROR_CLASS_PROPERTY; + error_code = ERROR_CODE_UNKNOWN_PROPERTY; + } else { + len = Analog_Output_Encode_Property_APDU( + &Temp_Buf[0], + object_instance, + temp_object_property, + array_index, + &error_class, &error_code); + } + break; + default: + len = -1; + error_class = ERROR_CLASS_OBJECT; + error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + break; + } + if (len < 0) { + len = rpm_ack_encode_apdu_object_property_error( + &Temp_Buf[0], + error_class, error_code); + apdu_len--; + error = true; + } + if (!copy_apdu_data_buffer(&apdu_len, len, npdu_len)) { + apdu_len = abort_encode_apdu( + &Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); + } + if (!error && property_found) { + len = encode_closing_tag(&Temp_Buf[0], 4); + if (!copy_apdu_data_buffer(&apdu_len, len, npdu_len)) { + apdu_len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); + break; + } + } else { + error = false; + } + /* check for closing tag */ + if (done) { + len = rpm_ack_decode_object_end( + &service_request[decode_len], + service_len - decode_len); + if (len == 1) { + decode_len++; + break; + } + } + } while(1); + len = encode_closing_tag(&Temp_Buf[0], 1); + if (!copy_apdu_data_buffer(&apdu_len, len, npdu_len)) { + apdu_len = abort_encode_apdu( + &Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); + break; + } + if (decode_len >= service_len) { + break; + } + } while(1); + } + pdu_len = apdu_len + npdu_len; + bytes_sent = datalink_send_pdu( + src, + &npdu_data, + &Handler_Transmit_Buffer[0], + pdu_len); +} diff --git a/bacnet-stack/demo/object/device.c b/bacnet-stack/demo/object/device.c index 55087674..40f9a914 100644 --- a/bacnet-stack/demo/object/device.c +++ b/bacnet-stack/demo/object/device.c @@ -47,6 +47,45 @@ #include "bacfile.h" /* object list dependency */ #endif +/* These three arrays are used by the ReadPropertyMultiple handler */ +static int Device_Properties_Required[] = +{ + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_DESCRIPTION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_REVISION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_OBJECT_LIST, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_DEVICE_ADDRESS_BINDING, + PROP_DATABASE_REVISION, + -1 +}; + +static int Device_Properties_Optional[] = +{ + -1 +}; + +static int Device_Properties_Proprietary[] = +{ + -1 +}; + /* note: you really only need to define variables for properties that are writable or that may change. The properties that are constant can be hard coded @@ -900,6 +939,100 @@ bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, return status; } +int int_list_count(const int *pList) +{ + int property_count = 0; + + while (*pList != -1) { + property_count++; + } + + return property_count; +} + +/* used to count the ALL, REQUIRED and OPTIONAL */ +int Device_Special_Property_Count(BACNET_PROPERTY_ID special_property) +{ + const int *pList = NULL; + int property_count = 0; + + if (special_property == PROP_ALL) { + property_count = + int_list_count(Device_Properties_Required); + property_count += + int_list_count(Device_Properties_Optional); + property_count += + int_list_count(Device_Properties_Proprietary); + } else if (special_property == PROP_REQUIRED) { + property_count = + int_list_count(Device_Properties_Required); + } else if (special_property == PROP_OPTIONAL) { + property_count = + int_list_count(Device_Properties_Optional); + } + + return property_count; +} + +/* returns the property ID given by the index into the list + or MAX_BACNET_PROPERTY_ID if not found or out of bounds */ +BACNET_PROPERTY_ID Device_Special_Property_By_Index( + BACNET_PROPERTY_ID special_property, /* ALL, OPTIONAL, REQUIRED */ + int index) +{ + BACNET_PROPERTY_ID property = MAX_BACNET_PROPERTY_ID; + int required = 0, optional = 0, proprietary = 0; + /* FIXME: we could use static vars to speed up access */ + + if (special_property == PROP_ALL) { + required = + property_list_count(Device_Properties_Required); + optional = + property_list_count(Device_Properties_Optional); + proprietary = + property_list_count(Device_Properties_Proprietary); + if (index < required) { + property = Device_Properties_Required[index]; + } else if (index < (required + optional)) { + property = Device_Properties_Optional[index]; + } else if (index < (required + optional + proprietary)) { + property = Device_Properties_Proprietary[index]; + } + } else if (special_property == PROP_REQUIRED) { + required = + property_list_count(Device_Properties_Required); + if (index < required) { + property = Device_Properties_Required[index]; + } + } else if (special_property == PROP_OPTIONAL) { + optional = + property_list_count(Device_Properties_Optional); + if (index < optional) { + property = Device_Properties_Optional[index]; + } + } + + return property; +} + +/* added for rpm service support when requesting prop_all, + prop_required, or prop_optional */ +bool Device_Property(uint8 *pIndex, BACNET_PROPERTY_ID *pProperty, BACNET_PROPERTY_ID special_property) +{ + bool result = false; + BACNET_PROPERTY_ID property; + + property = Device_Special_Property_By_Index( + special_property, *pIndex); + if (property != MAX_BACNET_PROPERTY_ID) { + (*pIndex)++; + *pProperty = property; + result = true; + } + + return result; +} + #ifdef TEST #include #include diff --git a/bacnet-stack/demo/object/device.h b/bacnet-stack/demo/object/device.h index c59bac37..43c69cc4 100644 --- a/bacnet-stack/demo/object/device.h +++ b/bacnet-stack/demo/object/device.h @@ -104,6 +104,20 @@ extern "C" { bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code); + /* support for RPM */ + + int Device_Special_Property_Count(BACNET_PROPERTY_ID special_property); + + /* returns the property ID given by the index into the list + or MAX_BACNET_PROPERTY_ID if not found or out of bounds */ + BACNET_PROPERTY_ID Device_Special_Property_By_Index( + BACNET_PROPERTY_ID special_property, /* ALL, OPTIONAL, REQUIRED */ + int index); + + bool Device_Property(uint8 *pIndex, BACNET_PROPERTY_ID *pProperty, + BACNET_PROPERTY_ID special_property); + + #ifdef __cplusplus } #endif /* __cplusplus */