Added encoding and decoding for ReadRange service, associated handlers and test application.

This commit is contained in:
petermcs
2009-09-25 21:56:49 +00:00
parent 6f254401c0
commit e8d91faaac
14 changed files with 2372 additions and 56 deletions
+1 -1
View File
@@ -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);
+155
View File
@@ -0,0 +1,155 @@
/**************************************************************************
*
* Copyright (C) 2009 Steve Karg <skarg@users.sourceforge.net>
*
* 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 <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#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;
}
+110
View File
@@ -0,0 +1,110 @@
/**************************************************************************
*
* Copyright (C) 2009 Steve Karg <skarg@users.sourceforge.net>
*
* 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 <stddef.h>
#include <stdint.h>
#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);
}
+107
View File
@@ -0,0 +1,107 @@
/**************************************************************************
*
* Copyright (C) 2008 Steve Karg <skarg@users.sourceforge.net>
*
* 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 <stddef.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#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;
}
+434
View File
@@ -0,0 +1,434 @@
/*************************************************************************
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* 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 <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h> /* for time */
#include <conio.h>
#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"
"<local-device-instance> 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"
"<remote-device-instance> 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;
}
@@ -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
File diff suppressed because it is too large Load Diff
+13 -3
View File
@@ -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
+14 -1
View File
@@ -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 */
+91
View File
@@ -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);
+4 -4
View File
@@ -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],
+14 -43
View File
@@ -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;
}
+370
View File
@@ -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 <stdint.h>
#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 */
+4 -4
View File
@@ -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,