diff --git a/bacnet-stack/demo/handler/h_rpm_a.c b/bacnet-stack/demo/handler/h_rpm_a.c index 5f43477d..efe9872e 100644 --- a/bacnet-stack/demo/handler/h_rpm_a.c +++ b/bacnet-stack/demo/handler/h_rpm_a.c @@ -106,7 +106,7 @@ static int rpm_ack_decode_service_request( rpm_property->value = value; old_value = value; while (value && (apdu_len > 0)) { - if (decode_is_context_specific(apdu)) { + if (IS_CONTEXT_SPECIFIC(*apdu)) { len = bacapp_decode_context_data(apdu, apdu_len, value, rpm_property->propertyIdentifier); diff --git a/bacnet-stack/demo/handler/h_rr.c b/bacnet-stack/demo/handler/h_rr.c new file mode 100644 index 00000000..5de6aa5a --- /dev/null +++ b/bacnet-stack/demo/handler/h_rr.c @@ -0,0 +1,155 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "readrange.h" + +static uint8_t Temp_Buf[MAX_APDU] = { 0 }; + +/* Encodes the property APDU and returns the length, + or sets the error, and returns -1 */ +int Encode_RR_payload( + uint8_t * apdu, + BACNET_READ_RANGE_DATA *pRequest, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = -1; + + /* FIXME: Stub function at the moment which just returns something + * for the sake of testing things out. Need to look at existing objects + * and see how we can do this for real. + */ + + pRequest->ItemCount = 6; + bitstring_init(&pRequest->ResultFlags); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, true); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, true); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, false); + pRequest->FirstSequence = 0; + + apdu_len = 0; + apdu_len += encode_application_unsigned(&apdu[apdu_len], 1); + apdu_len += encode_application_unsigned(&apdu[apdu_len], 2); + apdu_len += encode_application_unsigned(&apdu[apdu_len], 3); + apdu_len += encode_application_unsigned(&apdu[apdu_len], 4); + apdu_len += encode_application_unsigned(&apdu[apdu_len], 5); + apdu_len += encode_application_unsigned(&apdu[apdu_len], 6); + + return apdu_len; +} + +void handler_read_range( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_READ_RANGE_DATA data; + int len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + bool error = false; + int bytes_sent = 0; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + BACNET_ADDRESS my_address; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); +#if PRINT_ENABLED + fprintf(stderr, "RR: Segmented message. Sending Abort!\n"); +#endif + goto RR_ABORT; + } + + len = rr_decode_service_request(service_request, service_len, &data); +#if PRINT_ENABLED + if (len <= 0) + fprintf(stderr, "RR: Unable to decode Request!\n"); +#endif + if (len < 0) { + /* bad decoding - send an abort */ + len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, "RR: Bad Encoding. Sending Abort!\n"); +#endif + goto RR_ABORT; + } + + /* assume that there is an error */ + error = true; + len = Encode_RR_payload(&Temp_Buf[0], &data, &error_class, &error_code); + if (len >= 0) { + /* encode the APDU portion of the packet */ + data.application_data = &Temp_Buf[0]; + data.application_data_len = len; + /* FIXME: probably need a length limitation sent with encode */ + len = rr_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, &data); +#if PRINT_ENABLED + fprintf(stderr, "RR: Sending Ack!\n"); +#endif + error = false; + } + if (error) { + if (len == -2) { + /* BACnet APDU too small to fit data, so proper response is Abort */ + len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); +#if PRINT_ENABLED + fprintf(stderr, "RR: Reply too big to fit into APDU!\n"); +#endif + } else { + len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY, error_class, error_code); +#if PRINT_ENABLED + fprintf(stderr, "RR: Sending Error!\n"); +#endif + } + } + RR_ABORT: + pdu_len += len; + bytes_sent = datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno)); +#endif + + return; +} diff --git a/bacnet-stack/demo/handler/h_rr_a.c b/bacnet-stack/demo/handler/h_rr_a.c new file mode 100644 index 00000000..6e98488c --- /dev/null +++ b/bacnet-stack/demo/handler/h_rr_a.c @@ -0,0 +1,110 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 "config.h" +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "bactext.h" +#include "readrange.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" + +/* for debugging... */ +static void PrintReadRangeData( + BACNET_READ_RANGE_DATA * data) +{ + 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; +#if PRINT_ENABLED + fprintf(stdout, "{"); +#endif + print_brace = true; + } + bacapp_print_value(stdout, &value, data->object_property); + if (len) { + if (len < application_data_len) { + application_data += len; + application_data_len -= len; + /* there's more! */ +#if PRINT_ENABLED + fprintf(stdout, ","); +#endif + } else + break; + } else + break; + } +#if PRINT_ENABLED + if (print_brace) + fprintf(stdout, "}"); + fprintf(stdout, "\r\n"); +#endif + } +} + +void handler_read_range_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_READ_RANGE_DATA data; + + (void) src; + (void) service_data; /* we could use these... */ + len = rr_ack_decode_service_request(service_request, service_len, &data); + +#if PRINT_ENABLED + fprintf(stderr, "Received ReadRange Ack!\n"); +#endif + + if (len > 0) + PrintReadRangeData(&data); +} diff --git a/bacnet-stack/demo/handler/s_readrange.c b/bacnet-stack/demo/handler/s_readrange.c new file mode 100644 index 00000000..a0ef640f --- /dev/null +++ b/bacnet-stack/demo/handler/s_readrange.c @@ -0,0 +1,107 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* 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 "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "rpm.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "readrange.h" + +/* returns invoke id of 0 if device is not bound or no tsm available */ +uint8_t Send_ReadRange_Request( + uint32_t device_id, /* destination device */ + BACNET_READ_RANGE_DATA * read_access_data) +{ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, &npdu_data); + + /* encode the APDU portion of the packet */ + len = rr_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, read_access_data); + if (len <= 0) { + return 0; + } + + pdu_len += len; + /* is it small enough for the the destination to receive? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t)pdu_len); + bytes_sent = datalink_send_pdu(&dest, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, + "Failed to Send ReadRange Request (%s)!\n", strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, "Failed to Send ReadRange Request (exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/bacnet-stack/demo/readrange/main.c b/bacnet-stack/demo/readrange/main.c new file mode 100644 index 00000000..8b6ffbb5 --- /dev/null +++ b/bacnet-stack/demo/readrange/main.c @@ -0,0 +1,434 @@ +/************************************************************************* +* Copyright (C) 2006 Steve Karg +* +* 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. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include +#include /* for time */ +#include +#include "bacdef.h" +#include "config.h" +#include "bactext.h" +#include "bacerror.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" +#include "mydata.h" +#include "readrange.h" + +#if defined(__BORLANDC__) +#define _kbhit kbhit +#define _stricmp stricmp +#endif + +uint8_t Send_Private_Transfer_Request( + uint32_t device_id, + uint16_t vendor_id, + uint32_t service_number, + char block_number, + DATABLOCK *block); + + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; + +static int Target_Mode = 0; + +static BACNET_ADDRESS Target_Address; +static bool Error_Detected = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); +/* Error_Detected = true; */ +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\r\n", + bactext_abort_reason_name((int) abort_reason)); +/* Error_Detected = true; */ +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\r\n", + bactext_reject_reason_name((int) reject_reason)); +/* Error_Detected = true; */ +} + +static void Init_Service_Handlers( + void) +{ + /* we need to handle who-isx + 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); + + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_RANGE, handler_read_range); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, handler_conf_private_trans); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property_ack); + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_RANGE, handler_read_range_ack); + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, handler_conf_private_trans_ack); + + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); + apdu_set_error_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + uint8_t invoke_id = 0; + bool found = false; + BACNET_READ_RANGE_DATA Request; + int iCount = 0; + int iType = 0; + int iKey; + static int iLimit[3] = {7, 11, 7}; + + if (((argc != 2) && (argc != 3)) || ((argc >= 2) && (strcmp(argv[1], "--help") == 0))) { + printf("%s\n",argv[0]); + printf("Usage: %s server local-device-instance\r\n or\r\n" + " %s remote-device-instance\r\n" + "--help gives further information\r\n" + , filename_remove_path(argv[0]), filename_remove_path(argv[0])); + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("\r\nServer mode:\r\n\r\n" + " determins the device id of the application\r\n" + "when running as the server end of a test set up. The Server simply\r\n" + "returns dummy data for each ReadRange request\r\n\r\n" + "Non server:\r\n\r\n" + " indicates the device id of the server\r\n" + "instance of the application.\r\n" + "The non server application will send a series of ReadRange requests to the\r\n" + "server with examples of different range types.\r\n"); + } + return 0; + } + /* decode the command line parameters */ + if(_stricmp(argv[1], "server") == 0) + Target_Mode = 1; + else + Target_Mode = 0; + + Target_Device_Object_Instance = strtol(argv[1 + Target_Mode], NULL, 0); + + if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + + /* setup my info */ + if(Target_Mode) + Device_Set_Object_Instance_Number(Target_Device_Object_Instance); + else + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + + address_init(); + Init_Service_Handlers(); + dlenv_init(); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + + if(Target_Mode) { + printf("Entering server mode. press q to quit program\r\n\r\n"); + + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + if(current_seconds != last_seconds) { + } + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) { + putchar('.'); /* Just to show that time is passing... */ + last_seconds = current_seconds; + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); + } + + if(_kbhit()) { + iKey = toupper(_getch()); + if(iKey == 'Q') { + printf("\r\nExiting program now\r\n"); + exit(0); + } + } + } + } + else { + + /* try to bind with the device */ + found = address_bind_request(Target_Device_Object_Instance, &max_apdu, &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); + if (Error_Detected) + break; + /* wait until the device is bound, or timeout and quit */ + if(!found) + found = address_bind_request(Target_Device_Object_Instance, &max_apdu, &Target_Address); + if (found) { + if (invoke_id == 0) { /* Safe to send a new request */ + switch(iCount) { + case 0: + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 20; + Request.Count = 30; + Request.object_type = OBJECT_ANALOG_INPUT; + Request.object_instance = 0; + Request.object_property = PROP_PRESENT_VALUE; + Request.array_index = 1; + break; + + case 1: + Request.RequestType = RR_BY_SEQUENCE; + Request.Range.RefSeqNum = 20; + Request.Count = 30; + Request.object_type = OBJECT_ANALOG_INPUT; + Request.object_instance = 0; + Request.object_property = PROP_PRESENT_VALUE; + Request.array_index = 2; + break; + + case 2: + Request.RequestType = RR_BY_TIME; + Request.Range.RefTime.date.year = 2009; + Request.Range.RefTime.date.month = 9; + Request.Range.RefTime.date.day = 23; + Request.Range.RefTime.date.wday = 0xFF; /* Day of week unspecified */ + Request.Range.RefTime.time.hour = 22; + Request.Range.RefTime.time.min = 23; + Request.Range.RefTime.time.sec = 24; + Request.Range.RefTime.time.hundredths = 0; + + Request.Count = 30; + Request.object_type = OBJECT_ANALOG_INPUT; + Request.object_instance = 0; + Request.object_property = PROP_PRESENT_VALUE; + Request.array_index = 3; + break; + case 3: + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 20; + Request.Count = 30; + Request.object_type = OBJECT_ANALOG_INPUT; + Request.object_instance = 0; + Request.object_property = PROP_PRESENT_VALUE; + Request.array_index = BACNET_ARRAY_ALL; + break; + + case 4: + Request.RequestType = RR_BY_SEQUENCE; + Request.Range.RefSeqNum = 20; + Request.Count = 30; + Request.object_type = OBJECT_ANALOG_INPUT; + Request.object_instance = 0; + Request.object_property = PROP_PRESENT_VALUE; + Request.array_index = BACNET_ARRAY_ALL; + break; + + case 5: + Request.RequestType = RR_BY_TIME; + Request.Range.RefTime.date.year = 2009; + Request.Range.RefTime.date.month = 9; + Request.Range.RefTime.date.day = 23; + Request.Range.RefTime.date.wday = 0xFF; /* Day of week unspecified */ + Request.Range.RefTime.time.hour = 22; + Request.Range.RefTime.time.min = 23; + Request.Range.RefTime.time.sec = 24; + Request.Range.RefTime.time.hundredths = 0; + + Request.Count = 30; + Request.object_type = OBJECT_ANALOG_INPUT; + Request.object_instance = 0; + Request.object_property = PROP_PRESENT_VALUE; + Request.array_index = BACNET_ARRAY_ALL; + break; + + case 6: + Request.RequestType = RR_READ_ALL; + Request.Range.RefTime.date.year = 2009; + Request.Range.RefTime.date.month = 9; + Request.Range.RefTime.date.day = 23; + Request.Range.RefTime.date.wday = 0xFF; /* Day of week unspecified */ + Request.Range.RefTime.time.hour = 22; + Request.Range.RefTime.time.min = 23; + Request.Range.RefTime.time.sec = 24; + Request.Range.RefTime.time.hundredths = 0; + + Request.Count = 30; + Request.object_type = OBJECT_ANALOG_INPUT; + Request.object_instance = 0; + Request.object_property = PROP_PRESENT_VALUE; + Request.array_index = BACNET_ARRAY_ALL; + break; + + case 7: + Request.RequestType = RR_READ_ALL; + Request.Range.RefTime.date.year = 2009; + Request.Range.RefTime.date.month = 9; + Request.Range.RefTime.date.day = 23; + Request.Range.RefTime.date.wday = 0xFF; /* Day of week unspecified */ + Request.Range.RefTime.time.hour = 22; + Request.Range.RefTime.time.min = 23; + Request.Range.RefTime.time.sec = 24; + Request.Range.RefTime.time.hundredths = 0; + + Request.Count = 30; + Request.object_type = OBJECT_ANALOG_INPUT; + Request.object_instance = 0; + Request.object_property = PROP_PRESENT_VALUE; + Request.array_index = 7; + break; + } + + invoke_id = Send_ReadRange_Request( Target_Device_Object_Instance, &Request); + } + else if (tsm_invoke_id_free(invoke_id)) { + if(iCount != MY_MAX_BLOCK) { + iCount++; + invoke_id = 0; + } + else { + break; + } + } + else if (tsm_invoke_id_failed(invoke_id)) + { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(invoke_id); + /* Error_Detected = true; */ + /* try again or abort? */ + invoke_id = 0; /* Try next operation */ + /* break; */ + } + } + else + { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\r\n"); + /* Error_Detected = true; + break; */ + invoke_id = 0; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + } + + if (Error_Detected) + return 1; + return 0; +} diff --git a/bacnet-stack/demo/readrange/readrange/readrange.sln b/bacnet-stack/demo/readrange/readrange/readrange.sln new file mode 100644 index 00000000..c80b2c2f --- /dev/null +++ b/bacnet-stack/demo/readrange/readrange/readrange.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "readrange", "readrange.vcproj", "{6017A2EA-1296-4E67-995F-6344A3CC27C2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Debug|Win32.ActiveCfg = Debug|Win32 + {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Debug|Win32.Build.0 = Debug|Win32 + {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Release|Win32.ActiveCfg = Release|Win32 + {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/bacnet-stack/demo/readrange/readrange/readrange.vcproj b/bacnet-stack/demo/readrange/readrange/readrange.vcproj new file mode 100644 index 00000000..74460206 --- /dev/null +++ b/bacnet-stack/demo/readrange/readrange/readrange.vcproj @@ -0,0 +1,1035 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bacnet-stack/include/bacdcode.h b/bacnet-stack/include/bacdcode.h index 28aa7c27..b023e8c0 100644 --- a/bacnet-stack/include/bacdcode.h +++ b/bacnet-stack/include/bacdcode.h @@ -42,6 +42,7 @@ #include "bacstr.h" #include "bacint.h" #include "bacreal.h" +#include "bits.h" #ifdef __cplusplus extern "C" { @@ -75,9 +76,6 @@ extern "C" { uint32_t apdu_len_remaining, uint8_t * tag_number, uint32_t * value); -/* returns true if the tag is context specific */ - bool decode_is_context_specific( - uint8_t * apdu); /* returns true if the tag is an opening tag and matches */ bool decode_is_opening_tag_number( uint8_t * apdu, @@ -395,7 +393,19 @@ extern "C" { uint8_t invoke_id, uint8_t service_choice); +/* from clause 20.2.1.3.2 Constructed Data */ +/* true if extended tag numbering is used */ +#define IS_EXTENDED_TAG_NUMBER(x) ((x & 0xF0) == 0xF0) + +/* from clause 20.2.1.3.2 Constructed Data */ +/* true if the extended value is used */ +#define IS_EXTENDED_VALUE(x) ((x & 0x07) == 5) + +/* from clause 20.2.1.3.2 Constructed Data */ +/* true if the tag is context specific */ +#define IS_CONTEXT_SPECIFIC(x) ((x & BIT3) == BIT3) #ifdef __cplusplus + } #endif /* __cplusplus */ #endif diff --git a/bacnet-stack/include/handlers.h b/bacnet-stack/include/handlers.h index deec27d8..c2d1a8cb 100644 --- a/bacnet-stack/include/handlers.h +++ b/bacnet-stack/include/handlers.h @@ -35,6 +35,7 @@ #include "rpm.h" #include "wp.h" + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -211,7 +212,19 @@ extern "C" { uint16_t service_len, BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); -# + + void handler_read_range( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_read_range_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/bacnet-stack/include/readrange.h b/bacnet-stack/include/readrange.h new file mode 100644 index 00000000..bc6fa60a --- /dev/null +++ b/bacnet-stack/include/readrange.h @@ -0,0 +1,91 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2009 Peter Mc Shane + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +struct BACnet_Read_Range_Data; +typedef struct BACnet_Read_Range_Data { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + BACNET_PROPERTY_ID object_property; + uint32_t array_index; + uint8_t *application_data; + int application_data_len; + BACNET_BIT_STRING ResultFlags; /* FIRST_ITEM, LAST_ITEM, MORE_ITEMS */ + int RequestType; /* Index, sequence or time based request */ + uint32_t ItemCount; + uint32_t FirstSequence; + union { /* Pick the appropriate data type */ + uint32_t RefIndex; + uint32_t RefSeqNum; + BACNET_DATE_TIME RefTime; + } Range; + int32_t Count; /* SIGNED value as +ve vs -ve is important */ +} BACNET_READ_RANGE_DATA; + +/* Defines to indicate which type of read range request it is */ + +#define RR_BY_POSITION 0 +#define RR_BY_SEQUENCE 1 +#define RR_BY_TIME 2 +#define RR_READ_ALL 4 /* Read all of array - so don't send any range in the request */ + +/* Bit String Enumerations */ +typedef enum { + RESULT_FLAG_FIRST_ITEM = 0, + RESULT_FLAG_LAST_ITEM = 1, + RESULT_FLAG_MORE_ITEMS = 2 +} BACNET_RESULT_FLAGS; + +int rr_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_RANGE_DATA * rrdata); + +int rr_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_READ_RANGE_DATA * rrdata); + +int rr_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_RANGE_DATA * rrdata); + +int rr_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + BACNET_READ_RANGE_DATA * rrdata); + +uint8_t Send_ReadRange_Request( + uint32_t device_id, /* destination device */ + BACNET_READ_RANGE_DATA * read_access_data); \ No newline at end of file diff --git a/bacnet-stack/src/bacapp.c b/bacnet-stack/src/bacapp.c index 34bf86d5..166d12aa 100644 --- a/bacnet-stack/src/bacapp.c +++ b/bacnet-stack/src/bacapp.c @@ -266,7 +266,7 @@ int bacapp_decode_application_data( /* FIXME: use max_apdu_len! */ max_apdu_len = max_apdu_len; - if (apdu && value && !decode_is_context_specific(apdu)) { + if (apdu && value && !IS_CONTEXT_SPECIFIC(*apdu)) { value->context_specific = false; tag_len = decode_tag_number_and_value(&apdu[0], &tag_number, @@ -320,7 +320,7 @@ bool bacapp_decode_application_data_safe( apdu_len = 0; } - if (value && apdu_len_remaining > 0 && !decode_is_context_specific(&apdu[apdu_len]) ) { + if (value && apdu_len_remaining > 0 && !IS_CONTEXT_SPECIFIC(apdu[apdu_len]) ) { value->context_specific = false; tag_len = decode_tag_number_and_value_safe(&apdu[apdu_len], apdu_len_remaining, &tag_number, @@ -562,7 +562,7 @@ int bacapp_decode_context_data( /* FIXME: use max_apdu_len! */ max_apdu_len = max_apdu_len; - if (apdu && value && decode_is_context_specific(apdu)) { + if (apdu && value && IS_CONTEXT_SPECIFIC(*apdu)) { value->context_specific = true; tag_len = decode_tag_number_and_value(&apdu[0], &tag_number, @@ -735,7 +735,7 @@ int bacapp_data_len( &value); if (tag_number == opening_tag_number) opening_tag_number_counter--; - } else if (decode_is_context_specific(&apdu[apdu_len])) { + } else if (IS_CONTEXT_SPECIFIC(apdu[apdu_len])) { /* context-specific tagged data */ len = bacapp_decode_context_data(&apdu[apdu_len], diff --git a/bacnet-stack/src/bacdcode.c b/bacnet-stack/src/bacdcode.c index fe817f14..dd5dc57b 100644 --- a/bacnet-stack/src/bacdcode.c +++ b/bacnet-stack/src/bacdcode.c @@ -298,29 +298,7 @@ int encode_closing_tag( return len; } -/* from clause 20.2.1.3.2 Constructed Data */ -/* returns true if extended tag numbering is used */ -static bool decode_is_extended_tag_number( - uint8_t * apdu) -{ - return (bool) ((apdu[0] & 0xF0) == 0xF0); -} -/* from clause 20.2.1.3.2 Constructed Data */ -/* returns true if the extended value is used */ -static bool decode_is_extended_value( - uint8_t * apdu) -{ - return (bool) ((apdu[0] & 0x07) == 5); -} - -/* from clause 20.2.1.3.2 Constructed Data */ -/* returns true if the tag is context specific */ -bool decode_is_context_specific( - uint8_t * apdu) -{ - return (bool) ((apdu[0] & BIT3) == BIT3); -} int decode_tag_number( uint8_t * apdu, @@ -329,7 +307,7 @@ int decode_tag_number( int len = 1; /* return value */ /* decode the tag number first */ - if (decode_is_extended_tag_number(&apdu[0])) { + if (IS_EXTENDED_TAG_NUMBER(apdu[0])) { /* extended tag */ if (tag_number) { *tag_number = apdu[1]; @@ -354,7 +332,7 @@ int decode_tag_number_safe( /* decode the tag number first */ if ( apdu_len_remaining >= 1 ) { - if (decode_is_extended_tag_number(&apdu[0]) && apdu_len_remaining >= 2) { + if (IS_EXTENDED_TAG_NUMBER(apdu[0]) && apdu_len_remaining >= 2) { /* extended tag */ if (tag_number) { *tag_number = apdu[1]; @@ -397,7 +375,7 @@ int decode_tag_number_and_value( uint32_t value32; len = decode_tag_number(&apdu[0], tag_number); - if (decode_is_extended_value(&apdu[0])) { + if (IS_EXTENDED_VALUE(apdu[0])) { /* tagged as uint32_t */ if (apdu[len] == 255) { len++; @@ -447,7 +425,7 @@ int decode_tag_number_and_value_safe( if ( len > 0 ) { apdu_len_remaining -= len; - if (decode_is_extended_value(&apdu[0])) { + if (IS_EXTENDED_VALUE(apdu[0])) { /* tagged as uint32_t */ if (apdu[len] == 255 && apdu_len_remaining >= 5) { uint32_t value32; @@ -497,13 +475,9 @@ bool decode_is_context_tag( uint8_t tag_number) { uint8_t my_tag_number = 0; - bool context_specific = false; - - context_specific = decode_is_context_specific(apdu); decode_tag_number(apdu, &my_tag_number); - - return (bool) (context_specific && (my_tag_number == tag_number)); + return (bool) (IS_CONTEXT_SPECIFIC(*apdu) && (my_tag_number == tag_number)); } bool decode_is_context_tag_with_length( @@ -512,13 +486,10 @@ bool decode_is_context_tag_with_length( int *tag_length) { uint8_t my_tag_number = 0; - bool context_specific = false; - - context_specific = decode_is_context_specific(apdu); *tag_length = decode_tag_number(apdu, &my_tag_number); - return (bool) (context_specific && (my_tag_number == tag_number)); + return (bool) (IS_CONTEXT_SPECIFIC(*apdu) && (my_tag_number == tag_number)); } /* from clause 20.2.1.3.2 Constructed Data */ @@ -1877,7 +1848,7 @@ void testBACDCodeTags( for (tag_number = 0;; tag_number++) { len = encode_opening_tag(&apdu[0], tag_number); - test_len = get_apdu_len(decode_is_extended_tag_number(&apdu[0]), 0); + test_len = get_apdu_len(IS_EXTENDED_TAG_NUMBER(apdu[0]), 0); ct_test(pTest, len == test_len); len = decode_tag_number_and_value(&apdu[0], &test_tag_number, &value); ct_test(pTest, value == 0); @@ -1902,7 +1873,7 @@ void testBACDCodeTags( ct_test(pTest, tag_number == test_tag_number); ct_test(pTest, value == test_value); test_len = - get_apdu_len(decode_is_extended_tag_number(&apdu[0]), value); + get_apdu_len(IS_EXTENDED_TAG_NUMBER(apdu[0]), value); ct_test(pTest, len == test_len); /* stop at the the last value */ if (value & BIT31) { @@ -1948,10 +1919,10 @@ void testBACDCodeEnumerated( len = decode_tag_number_and_value(&apdu[0], &tag_number, NULL); ct_test(pTest, len == 1); ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_ENUMERATED); - ct_test(pTest, decode_is_context_specific(&apdu[0]) == false); + ct_test(pTest, IS_CONTEXT_SPECIFIC(apdu[0]) == false); /* context specific encoding */ apdu_len = encode_context_enumerated(&apdu[0], 3, value); - ct_test(pTest, decode_is_context_specific(&apdu[0]) == true); + ct_test(pTest, IS_CONTEXT_SPECIFIC(apdu[0]) == true); len = decode_tag_number_and_value(&apdu[0], &tag_number, NULL); ct_test(pTest, len == 1); ct_test(pTest, tag_number == 3); @@ -1987,7 +1958,7 @@ void testBACDCodeReal( /* len tells us how many octets were used for encoding the value */ len = decode_tag_number_and_value(&apdu[0], &tag_number, &long_value); ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_REAL); - ct_test(pTest, decode_is_context_specific(&apdu[0]) == false); + ct_test(pTest, IS_CONTEXT_SPECIFIC(apdu[0]) == false); ct_test(pTest, len == 1); ct_test(pTest, long_value == 4); decode_real(&apdu[len], &decoded_value); @@ -2021,7 +1992,7 @@ static void testBACDCodeDouble( /* len tells us how many octets were used for encoding the value */ len = decode_tag_number_and_value(&apdu[0], &tag_number, &long_value); ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_DOUBLE); - ct_test(pTest, decode_is_context_specific(&apdu[0]) == false); + ct_test(pTest, IS_CONTEXT_SPECIFIC(apdu[0]) == false); ct_test(pTest, len == 2); ct_test(pTest, long_value == 8); decode_double(&apdu[len], &decoded_value); @@ -2060,7 +2031,7 @@ void testBACDCodeUnsignedValue( len = decode_tag_number_and_value(&apdu[0], &tag_number, NULL); ct_test(pTest, len == 1); ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_UNSIGNED_INT); - ct_test(pTest, decode_is_context_specific(&apdu[0]) == false); + ct_test(pTest, IS_CONTEXT_SPECIFIC(apdu[0]) == false); } void testBACDCodeUnsigned( @@ -2131,7 +2102,7 @@ void testBACDCodeSignedValue( apdu_len = encode_application_signed(&apdu[0], value); len = decode_tag_number_and_value(&apdu[0], &tag_number, NULL); ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_SIGNED_INT); - ct_test(pTest, decode_is_context_specific(&apdu[0]) == false); + ct_test(pTest, IS_CONTEXT_SPECIFIC(apdu[0]) == false); return; } diff --git a/bacnet-stack/src/readrange.c b/bacnet-stack/src/readrange.c new file mode 100644 index 00000000..1d4b339a --- /dev/null +++ b/bacnet-stack/src/readrange.c @@ -0,0 +1,370 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2009 Peter Mc Shane + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "readrange.h" + +/* + * ReadRange-Request ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + * range CHOICE { + * byPosition [3] SEQUENCE { + * referenceIndex Unsigned, + * count INTEGER + * }, + * -- context tag 4 is deprecated + * -- context tag 5 is deprecated + * bySequenceNumber [6] SEQUENCE { + * referenceIndex Unsigned, + * count INTEGER + * }, + * byTime [7] SEQUENCE { + * referenceTime BACnetDateTime, + * count INTEGER + * } + * } OPTIONAL + * } + */ + +/***************************************************************************** + * Build a ReadRange request packet. * + *****************************************************************************/ + +int rr_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_RANGE_DATA * rrdata) +{ + int apdu_len; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_READ_RANGE; /* service choice */ + apdu_len = 4; + + apdu_len += encode_context_object_id(&apdu[apdu_len], 0, rrdata->object_type, rrdata->object_instance); + apdu_len += encode_context_enumerated(&apdu[apdu_len], 1, rrdata->object_property); + + /* optional array index */ + + if (rrdata->array_index != BACNET_ARRAY_ALL) { + apdu_len += encode_context_unsigned(&apdu[apdu_len], 2, rrdata->array_index); + } + + /* Build the appropriate (optional) range parameter based on the request type */ + + switch (rrdata->RequestType) { + case RR_BY_POSITION: + apdu_len += encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += encode_application_unsigned(&apdu[apdu_len], rrdata->Range.RefIndex); + apdu_len += encode_application_signed(&apdu[apdu_len], rrdata->Count); + apdu_len += encode_closing_tag(&apdu[apdu_len], 3); + break; + + case RR_BY_SEQUENCE: + apdu_len += encode_opening_tag(&apdu[apdu_len], 6); + apdu_len += encode_application_unsigned(&apdu[apdu_len], rrdata->Range.RefSeqNum); + apdu_len += encode_application_signed(&apdu[apdu_len], rrdata->Count); + apdu_len += encode_closing_tag(&apdu[apdu_len], 6); + break; + + case RR_BY_TIME: + apdu_len += encode_opening_tag(&apdu[apdu_len], 7); + apdu_len += encode_application_date(&apdu[apdu_len], &rrdata->Range.RefTime.date); + apdu_len += encode_application_time(&apdu[apdu_len], &rrdata->Range.RefTime.time); + apdu_len += encode_application_signed(&apdu[apdu_len], rrdata->Count); + apdu_len += encode_closing_tag(&apdu[apdu_len], 7); + break; + + case RR_READ_ALL: /* to attempt a read of the whole array or list, omit the range parameter */ + break; + + default: + break; + } + } + + return apdu_len; +} + +/***************************************************************************** + * Decode the received ReadRange request * + *****************************************************************************/ + +int rr_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_READ_RANGE_DATA * rrdata) +{ + unsigned len = 0; + unsigned TagLen = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint16_t type = 0; /* for decoding */ + uint32_t UnsignedTemp; + + /* check for value pointers */ + if (apdu_len && rrdata) { + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[len++], 0)) + return -1; + len += decode_object_id(&apdu[len], &type, &rrdata->object_instance); + rrdata->object_type = (BACNET_OBJECT_TYPE) type; + /* Tag 1: Property ID */ + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number != 1) + return -1; + len += decode_enumerated(&apdu[len], len_value_type, &UnsignedTemp); + rrdata->object_property = (BACNET_PROPERTY_ID) UnsignedTemp; + /* Tag 2: Optional Array Index */ + rrdata->array_index = BACNET_ARRAY_ALL; /* Assuming this is the most common outcome... */ + if (len < apdu_len) { + TagLen = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number == 2) { + len += TagLen; + len += decode_unsigned(&apdu[len], len_value_type, &UnsignedTemp); + rrdata->array_index = UnsignedTemp; + } + } + /* And/or optional range selection- Tags 3, 6 and 7 */ + rrdata->RequestType = RR_READ_ALL; /* Assume the worst to cut out explicit checking later */ + if (len < apdu_len) { + /* + * Note: We pick up the opening tag and then decode the parameter types we recognise. + * We deal with the count and the closing tag in each case statement even though it + * might appear that we could do them after the switch statement as common elements. + * This is so that if we receive a tag we don't recognise, we don't try to decode it + * blindly and make a mess of it. + */ + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + switch(tag_number) { + case 3: /* ReadRange by position */ + rrdata->RequestType = RR_BY_POSITION; + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + len += decode_unsigned(&apdu[len], len_value_type, &rrdata->Range.RefIndex); + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + len += decode_signed(&apdu[len], len_value_type, &rrdata->Count); + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + break; + + case 6: /* ReadRange by sequence number */ + rrdata->RequestType = RR_BY_SEQUENCE; + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + len += decode_unsigned(&apdu[len], len_value_type, &rrdata->Range.RefSeqNum); + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + len += decode_signed(&apdu[len], len_value_type, &rrdata->Count); + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + break; + + case 7: /* ReadRange by time stamp */ + rrdata->RequestType = RR_BY_TIME; + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + len += decode_date(&apdu[len], &rrdata->Range.RefTime.date); + len += decode_bacnet_time(&apdu[len], &rrdata->Range.RefTime.time); + len += decode_unsigned(&apdu[len], len_value_type, &rrdata->Range.RefIndex); + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + len += decode_signed(&apdu[len], len_value_type, &rrdata->Count); + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + break; + + default: /* If we don't recognise the tag then we do nothing here and try to return + * all elements of the array */ + break; + } + } + } + + return (int) len; +} + +/* + * ReadRange-ACK ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL , -- used only with array datatype + * resultFlags [3] BACnetResultFlags, + * itemCount [4] Unsigned, + * itemData [5] SEQUENCE OF ABSTRACT-SYNTAX.&TYPE, + * firstSequenceNumber [6] Unsigned32 OPTIONAL -- used only if 'Item Count' > 0 and the request was either of + * -- type 'By Sequence Number' or 'By Time' + * } + */ + +/***************************************************************************** + * Build a ReadRange response packet * + *****************************************************************************/ + +int rr_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_RANGE_DATA * rrdata) +{ + int len = 0; /* length of each encoding */ + int apdu_len; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ + apdu[1] = invoke_id; /* original invoke id from request */ + apdu[2] = SERVICE_CONFIRMED_READ_RANGE; /* service choice */ + apdu_len = 3; + /* service ack follows */ + apdu_len += encode_context_object_id(&apdu[apdu_len], 0, rrdata->object_type, rrdata->object_instance); + apdu_len += encode_context_enumerated(&apdu[apdu_len], 1, rrdata->object_property); + /* context 2 array index is optional */ + if (rrdata->array_index != BACNET_ARRAY_ALL) { + apdu_len += encode_context_unsigned(&apdu[apdu_len], 2, rrdata->array_index); + } + /* Context 3 BACnet Result Flags */ + apdu_len += encode_context_bitstring(&apdu[apdu_len], 3, &rrdata->ResultFlags); + /* Context 4 Item Count */ + apdu_len += encode_context_unsigned(&apdu[apdu_len], 4, rrdata->ItemCount); + /* Context 5 Property list - reading the standard it looks like an empty list still + * requires an opening and closing tag as the tagged parameter is not optional + */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 5); + if(rrdata->ItemCount != 0) { + for (len = 0; len < rrdata->application_data_len; len++) { + apdu[apdu_len++] = rrdata->application_data[len]; + } + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 5); + + if((rrdata->ItemCount != 0) && (rrdata->RequestType != RR_BY_POSITION) && (rrdata->RequestType != RR_READ_ALL)) { + /* Context 6 Sequence number of first item */ + apdu_len += encode_context_unsigned(&apdu[apdu_len], 6, rrdata->FirstSequence); + } + } + + return apdu_len; +} + +/***************************************************************************** + * Decode the received ReadRange response * + *****************************************************************************/ + +int rr_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + BACNET_READ_RANGE_DATA * rrdata) +{ + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + int tag_len = 0; /* length of tag decode */ + int len = 0; /* total length of decodes */ + int start_len; + uint16_t object = 0; /* object type */ + uint32_t property = 0; /* for decoding */ + uint32_t array_value = 0; /* for decoding */ + + /* FIXME: check apdu_len against the len during decode */ + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[0], 0)) + return -1; + len = 1; + len += decode_object_id(&apdu[len], &object, &rrdata->object_instance); + rrdata->object_type = (BACNET_OBJECT_TYPE) object; + + /* Tag 1: Property ID */ + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number != 1) + return -1; + len += decode_enumerated(&apdu[len], len_value_type, &property); + rrdata->object_property = (BACNET_PROPERTY_ID) property; + + /* Tag 2: Optional Array Index */ + tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number == 2) { + len += tag_len; + len += decode_unsigned(&apdu[len], len_value_type, &array_value); + rrdata->array_index = array_value; + } else + rrdata->array_index = BACNET_ARRAY_ALL; + + /* Tag 3: Result Flags */ + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number != 3) + return -1; + + len += decode_bitstring(&apdu[len], len_value_type, &rrdata->ResultFlags); + + /* Tag 4: Item count */ + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number != 4) + return -1; + + len += decode_unsigned(&apdu[len], len_value_type, &rrdata->ItemCount); + + if (decode_is_opening_tag_number(&apdu[len], 5)) { + len++; /* a tag number of 5 is not extended so only one octet */ + /* Setup the start position and length of the data returned from the request + * don't decode the application tag number or its data here */ + rrdata->application_data = &apdu[len]; + start_len = len; + while(len < apdu_len) { + if(IS_CONTEXT_SPECIFIC(apdu[len]) && (decode_is_closing_tag_number(&apdu[len], 5))) { + rrdata->application_data_len = len - start_len; + len++; /* Step over single byte closing tag */ + break; + } + else { + /* Don't care about tag number, just skipping over anyway */ + len += decode_tag_number_and_value(&apdu[len], NULL, &len_value_type); + len += len_value_type; /* Skip over data value as well */ + if(len >= apdu_len) /* APDU is exhausted so we have failed to find closing tag */ + return(-1); + } + } + } else { + return -1; + } + if(len < apdu_len) { /* Still something left to look at? */ + /* Tag 6: Item count */ + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number != 6) + return -1; + + len += decode_unsigned(&apdu[len], len_value_type, &rrdata->FirstSequence); + } + + len = apdu_len; /* There should be nothing left to see here */ + return len; +} + +/* FIXME: Currently does not have test framework */ diff --git a/bacnet-stack/src/rpm.c b/bacnet-stack/src/rpm.c index cccea93e..d32bfd30 100644 --- a/bacnet-stack/src/rpm.c +++ b/bacnet-stack/src/rpm.c @@ -254,7 +254,7 @@ int rpm_decode_object_property( /* check for valid pointers */ if (apdu && apdu_len && object_property && array_index) { /* Tag 0: propertyIdentifier */ - if (!decode_is_context_specific(&apdu[len])) + if (!IS_CONTEXT_SPECIFIC(apdu[len])) return -1; len += decode_tag_number_and_value(&apdu[len], &tag_number, @@ -265,7 +265,7 @@ int rpm_decode_object_property( if (object_property) *object_property = (BACNET_PROPERTY_ID) property; /* Tag 1: Optional propertyArrayIndex */ - if ((len < apdu_len) && decode_is_context_specific(&apdu[len]) && + if ((len < apdu_len) && IS_CONTEXT_SPECIFIC(apdu[len]) && (!decode_is_closing_tag(&apdu[len]))) { option_len = (unsigned) decode_tag_number_and_value(&apdu[len], &tag_number, @@ -447,7 +447,7 @@ int rpm_ack_decode_object_property( /* check for valid pointers */ if (apdu && apdu_len && object_property && array_index) { /* Tag 2: propertyIdentifier */ - if (!decode_is_context_specific(&apdu[len])) + if (!IS_CONTEXT_SPECIFIC(apdu[len])) return -1; len += decode_tag_number_and_value(&apdu[len], &tag_number, @@ -458,7 +458,7 @@ int rpm_ack_decode_object_property( if (object_property) *object_property = (BACNET_PROPERTY_ID) property; /* Tag 3: Optional propertyArrayIndex */ - if ((len < apdu_len) && decode_is_context_specific(&apdu[len]) && + if ((len < apdu_len) && IS_CONTEXT_SPECIFIC(apdu[len]) && (!decode_is_closing_tag(&apdu[len]))) { tag_len = (unsigned) decode_tag_number_and_value(&apdu[len], &tag_number,