Added encoding and decoding for ReadRange service, associated handlers and test application.
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user