From 7921d2f811edb7315716b9967dc20204b0ec41f5 Mon Sep 17 00:00:00 2001 From: skarg Date: Sun, 23 Nov 2008 22:25:08 +0000 Subject: [PATCH] added ReadPropertyMultiple client demo application, bacrpm. --- bacnet-stack/Makefile | 7 +- bacnet-stack/demo/handler/h_rpm.c | 40 ++--- bacnet-stack/demo/handler/h_rpm_a.c | 246 ++++++++++++++++++++++++++++ bacnet-stack/demo/handler/s_rpm.c | 117 +++++++++++++ bacnet-stack/include/bacapp.h | 19 +++ bacnet-stack/include/client.h | 6 + bacnet-stack/include/cov.h | 15 -- bacnet-stack/include/handlers.h | 6 + bacnet-stack/include/memcopy.h | 60 +++++++ bacnet-stack/include/rpm.h | 17 +- bacnet-stack/include/sbuf.h | 24 +-- bacnet-stack/lib/Makefile | 3 + bacnet-stack/lib/bacnet.cbp | 24 ++- bacnet-stack/lib/bacnetdll.cbp | 9 +- bacnet-stack/lib/makefile.b32 | 3 + bacnet-stack/makefile.b32 | 14 +- bacnet-stack/src/bacdcode.c | 2 +- bacnet-stack/src/memcopy.c | 110 +++++++++++++ bacnet-stack/src/rpm.c | 70 ++++++++ bacnet-stack/src/sbuf.c | 19 ++- bacnet-stack/test.mak | 6 + bacnet-stack/test/memcopy.mak | 32 ++++ 22 files changed, 772 insertions(+), 77 deletions(-) create mode 100644 bacnet-stack/demo/handler/h_rpm_a.c create mode 100644 bacnet-stack/demo/handler/s_rpm.c create mode 100644 bacnet-stack/include/memcopy.h create mode 100644 bacnet-stack/src/memcopy.c create mode 100644 bacnet-stack/test/memcopy.mak diff --git a/bacnet-stack/Makefile b/bacnet-stack/Makefile index 35ba57fc..40e01b1b 100644 --- a/bacnet-stack/Makefile +++ b/bacnet-stack/Makefile @@ -1,10 +1,11 @@ all: library readprop writeprop readfile writefile reinit server dcc \ - whohas whois ucov timesync epics mstpcap \ + whohas whois ucov timesync epics readpropm mstpcap \ whoisrouter iamrouter initrouter @echo "utilities are in the bin directory" clean: lib/Makefile\ demo/readprop/Makefile \ + demo/readpropm/Makefile \ demo/writeprop/Makefile \ demo/readfile/Makefile \ demo/writefile/Makefile \ @@ -22,6 +23,7 @@ clean: lib/Makefile\ demo/mstpcap/Makefile ( cd lib ; make clean ) ( cd demo/readprop ; make clean ) + ( cd demo/readpropm ; make clean ) ( cd demo/writeprop ; make clean ) ( cd demo/readfile ; make clean ) ( cd demo/writefile ; make clean ) @@ -44,6 +46,9 @@ library: lib/Makefile readprop: demo/readprop/Makefile ( cd demo/readprop ; make ; cp bacrp ../../bin ) +readpropm: demo/readpropm/Makefile + ( cd demo/readpropm ; make ; cp bacrpm ../../bin ) + writeprop: demo/writeprop/Makefile ( cd demo/writeprop ; make ; cp bacwp ../../bin ) diff --git a/bacnet-stack/demo/handler/h_rpm.c b/bacnet-stack/demo/handler/h_rpm.c index a1b2d8e0..5ab5715c 100644 --- a/bacnet-stack/demo/handler/h_rpm.c +++ b/bacnet-stack/demo/handler/h_rpm.c @@ -1,7 +1,7 @@ /************************************************************************** * * Copyright (C) 2007 Steve Karg -* Inspired by John Stachler +* Inspired by John Stachler * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -30,6 +30,7 @@ #include #include "config.h" #include "txbuf.h" +#include "memcopy.h" #include "bacdef.h" #include "bacdcode.h" #include "apdu.h" @@ -210,27 +211,6 @@ static unsigned RPM_Object_Property_Count( return count; } -/* copy len bytes from src to offset of dest if there is enough space. */ -int apdu_copy( - uint8_t * dest, - uint8_t * src, - int offset, - int len, - int max) -{ - int i; - int copy_len = 0; - - if (len <= (max - offset)) { - for (i = 0; i < len; i++) { - dest[offset + i] = src[i]; - copy_len++; - } - } - - return copy_len; -} - /* Encode the RPM property returning the length of the encoding, or 0 if there is no room to fit the encoding. */ int RPM_Encode_Property( @@ -250,9 +230,10 @@ int RPM_Encode_Property( len = rpm_ack_encode_apdu_object_property(&Temp_Buf[0], object_property, array_index); - len = apdu_copy(&apdu[0], &Temp_Buf[0], offset, len, max_apdu); - if (!len) + len = memcopy(&apdu[0], &Temp_Buf[0], offset, len, max_apdu); + if (!len) { return 0; + } apdu_len += len; len = Encode_Property_APDU(&Temp_Buf[0], object_type, object_instance, @@ -263,10 +244,11 @@ int RPM_Encode_Property( rpm_ack_encode_apdu_object_property_error(&Temp_Buf[0], error_class, error_code); len = - apdu_copy(&apdu[0], &Temp_Buf[0], offset + apdu_len, len, + memcopy(&apdu[0], &Temp_Buf[0], offset + apdu_len, len, max_apdu); - if (!len) + if (!len) { return 0; + } } else if ((offset + apdu_len + 1 + len + 1) < max_apdu) { /* enough room to fit the property value and tags */ len = @@ -339,7 +321,7 @@ void handler_read_property_multiple( decode_len++; len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]); copy_len = - apdu_copy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], + memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], apdu_len, len, sizeof(Handler_Transmit_Buffer)); if (!copy_len) { apdu_len = @@ -362,7 +344,7 @@ void handler_read_property_multiple( rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], object_type, object_instance); copy_len = - apdu_copy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], + memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], apdu_len, len, sizeof(Handler_Transmit_Buffer)); if (!copy_len) { apdu_len = @@ -389,7 +371,7 @@ void handler_read_property_multiple( decode_len++; len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]); copy_len = - apdu_copy(&Handler_Transmit_Buffer[npdu_len], + memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], apdu_len, len, sizeof(Handler_Transmit_Buffer)); if (!copy_len) { diff --git a/bacnet-stack/demo/handler/h_rpm_a.c b/bacnet-stack/demo/handler/h_rpm_a.c new file mode 100644 index 00000000..ec4016fd --- /dev/null +++ b/bacnet-stack/demo/handler/h_rpm_a.c @@ -0,0 +1,246 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include "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 "rpm.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" + +/* returns the number of bytes decoded, or -1 on error */ +/* note: initial the linked list of read_access_data */ +static int rpm_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + BACNET_READ_ACCESS_DATA * read_access_data) +{ + int decoded_len = 0; /* return value */ + int len = 0; /* number of bytes returned from decoding */ + BACNET_READ_ACCESS_DATA *rpm_object; + BACNET_READ_ACCESS_DATA *old_rpm_object; + BACNET_PROPERTY_REFERENCE *rpm_property; + BACNET_PROPERTY_REFERENCE *old_rpm_property; + BACNET_APPLICATION_DATA_VALUE *value; + BACNET_APPLICATION_DATA_VALUE *old_value; + + rpm_object = read_access_data; + old_rpm_object = rpm_object; + while (rpm_object && apdu_len) { + len = rpm_ack_decode_object_id( + apdu, apdu_len, + &rpm_object->object_type, + &rpm_object->object_instance); + if (len <= 0) { + old_rpm_object->next = NULL; + free(rpm_object); + break; + } + decoded_len += len; + apdu_len -= len; + apdu += len; + rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); + rpm_object->listOfProperties = rpm_property; + old_rpm_property = rpm_property; + while (rpm_property && apdu_len) { + len = rpm_ack_decode_object_property( + apdu, + apdu_len, + &rpm_property->propertyIdentifier, + &rpm_property->propertyArrayIndex); + if (len <= 0) { + old_rpm_property->next = NULL; + free(rpm_property); + break; + } + decoded_len += len; + apdu_len -= len; + apdu += len; + if (apdu_len && decode_is_opening_tag_number(apdu, 4)) { + decoded_len++; + apdu_len--; + apdu++; + /* note: if this is an array, there will be + more than one element to decode */ + value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE)); + rpm_property->value = value; + old_value = value; + while (value && (apdu_len > 0)) { + len = bacapp_decode_application_data( + apdu, + apdu_len, + value); + decoded_len += len; + apdu_len -= len; + apdu += len; + if (apdu_len && decode_is_closing_tag_number(apdu, 4)) { + decoded_len++; + apdu_len--; + apdu++; + break; + } else { + old_value = value; + value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE)); + old_value->next = value; + } + } + } + old_rpm_property = rpm_property; + rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); + old_rpm_property->next = rpm_property; + } + len = rpm_decode_object_end(apdu, apdu_len); + if (len) { + decoded_len += len; + apdu_len -= len; + apdu += len; + } + if (apdu_len) { + old_rpm_object = rpm_object; + rpm_object = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + old_rpm_object->next = rpm_object; + } + } + + return decoded_len; +} + +/* for debugging... */ +static void PrintReadPropertyMultipleData( + BACNET_READ_ACCESS_DATA * rpm_data) +{ + BACNET_PROPERTY_REFERENCE *listOfProperties; + BACNET_APPLICATION_DATA_VALUE *value; + bool array_value = false; + + if (rpm_data) { +#if PRINT_ENABLED + fprintf(stdout, "%s #%u\r\n", + bactext_object_type_name(rpm_data->object_type), + rpm_data->object_instance); + fprintf(stdout, "{\r\n"); +#endif + listOfProperties = rpm_data->listOfProperties; + while (listOfProperties) { +#if PRINT_ENABLED + fprintf(stdout, " %s: ", + bactext_property_name(listOfProperties->propertyIdentifier)); +#endif + if (listOfProperties->propertyArrayIndex != BACNET_ARRAY_ALL) { +#if PRINT_ENABLED + fprintf(stdout, "[%d]", listOfProperties->propertyArrayIndex); +#endif + } + value = listOfProperties->value; +#if PRINT_ENABLED + if (value->next) { + fprintf(stdout, "{"); + array_value = true; + } else { + array_value = false; + } +#endif + while (value) { + bacapp_print_value(stdout, + value, + listOfProperties->propertyIdentifier); +#if PRINT_ENABLED + if (value->next) { + fprintf(stdout, ",\r\n "); + } else { + if (array_value) { + fprintf(stdout, "}\r\n"); + } else { + fprintf(stdout, "\r\n"); + } + } +#endif + value = value->next; + } + listOfProperties = listOfProperties->next; + } +#if PRINT_ENABLED + fprintf(stdout, "}\r\n"); +#endif + } +} + +void handler_read_property_multiple_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_READ_ACCESS_DATA * rpm_data; + BACNET_READ_ACCESS_DATA * old_rpm_data; + BACNET_PROPERTY_REFERENCE *rpm_property; + BACNET_PROPERTY_REFERENCE *old_rpm_property; + BACNET_APPLICATION_DATA_VALUE *value; + BACNET_APPLICATION_DATA_VALUE *old_value; + + (void) src; + (void) service_data; /* we could use these... */ + + rpm_data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + if (rpm_data) { + len = rpm_ack_decode_service_request(service_request, service_len, + rpm_data); + } +#if 1 + fprintf(stderr, "Received Read-Property-Multiple Ack!\n"); +#endif + if (len > 0) { + while (rpm_data) { + PrintReadPropertyMultipleData(rpm_data); + rpm_property = rpm_data->listOfProperties; + while (rpm_property) { + value = rpm_property->value; + while (value) { + old_value = value; + value = value->next; + free(old_value); + } + old_rpm_property = rpm_property; + rpm_property = rpm_property->next; + free(old_rpm_property); + } + old_rpm_data = rpm_data; + rpm_data = rpm_data->next; + free(old_rpm_data); + } + } +} diff --git a/bacnet-stack/demo/handler/s_rpm.c b/bacnet-stack/demo/handler/s_rpm.c new file mode 100644 index 00000000..76321681 --- /dev/null +++ b/bacnet-stack/demo/handler/s_rpm.c @@ -0,0 +1,117 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "rpm.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "sbuf.h" + +/* returns invoke id of 0 if device is not bound or no tsm available */ +uint8_t Send_Read_Property_Multiple_Request( + uint8_t * pdu, + size_t max_pdu, + uint32_t device_id, /* destination device */ + BACNET_READ_ACCESS_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(&pdu[0], &dest, &my_address, + &npdu_data); + /* encode the APDU portion of the packet */ + len = rpm_encode_apdu( + &pdu[pdu_len], + max_pdu - 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, &pdu[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &pdu[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, + "Failed to Send ReadPropertyMultiple Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send ReadPropertyMultiple Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/bacnet-stack/include/bacapp.h b/bacnet-stack/include/bacapp.h index ab34040b..a34c2922 100644 --- a/bacnet-stack/include/bacapp.h +++ b/bacnet-stack/include/bacapp.h @@ -89,6 +89,25 @@ typedef struct BACnet_Application_Data_Value { struct BACnet_Application_Data_Value *next; } BACNET_APPLICATION_DATA_VALUE; +struct BACnet_Property_Reference; +typedef struct BACnet_Property_Reference { + BACNET_PROPERTY_ID propertyIdentifier; + int32_t propertyArrayIndex; /* optional */ + BACNET_APPLICATION_DATA_VALUE *value; + /* simple linked list */ + struct BACnet_Property_Reference *next; +} BACNET_PROPERTY_REFERENCE; + +struct BACnet_Property_Value; +typedef struct BACnet_Property_Value { + BACNET_PROPERTY_ID propertyIdentifier; + int32_t propertyArrayIndex; + BACNET_APPLICATION_DATA_VALUE value; + uint8_t priority; + /* simple linked list */ + struct BACnet_Property_Value *next; +} BACNET_PROPERTY_VALUE; + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ diff --git a/bacnet-stack/include/client.h b/bacnet-stack/include/client.h index 54684ea5..613e8db8 100644 --- a/bacnet-stack/include/client.h +++ b/bacnet-stack/include/client.h @@ -33,6 +33,7 @@ #include "npdu.h" #include "bacapp.h" #include "bacenum.h" +#include "rpm.h" #ifdef __cplusplus extern "C" { @@ -74,6 +75,11 @@ extern "C" { uint32_t object_instance, BACNET_PROPERTY_ID object_property, int32_t array_index); + uint8_t Send_Read_Property_Multiple_Request( + uint8_t * pdu, + size_t max_pdu, + uint32_t device_id, /* destination device */ + BACNET_READ_ACCESS_DATA *read_access_data); /* returns the invoke ID for confirmed request, or 0 if failed */ uint8_t Send_Write_Property_Request( diff --git a/bacnet-stack/include/cov.h b/bacnet-stack/include/cov.h index a39d659a..90c0b58e 100644 --- a/bacnet-stack/include/cov.h +++ b/bacnet-stack/include/cov.h @@ -38,16 +38,6 @@ #include #include "bacapp.h" -struct BACnet_Property_Value; -typedef struct BACnet_Property_Value { - BACNET_PROPERTY_ID propertyIdentifier; - uint32_t propertyArrayIndex; - BACNET_APPLICATION_DATA_VALUE value; - uint8_t priority; - /* simple linked list */ - struct BACnet_Property_Value *next; -} BACNET_PROPERTY_VALUE; - typedef struct BACnet_COV_Data { uint32_t subscriberProcessIdentifier; uint32_t initiatingDeviceIdentifier; @@ -57,11 +47,6 @@ typedef struct BACnet_COV_Data { BACNET_PROPERTY_VALUE *listOfValues; } BACNET_COV_DATA; -typedef struct BACnet_Property_Reference { - BACNET_PROPERTY_ID propertyIdentifier; - unsigned propertyArrayIndex; /* optional */ -} BACNET_PROPERTY_REFERENCE; - typedef struct BACnet_Subscribe_COV_Data { uint32_t subscriberProcessIdentifier; BACNET_OBJECT_ID monitoredObjectIdentifier; diff --git a/bacnet-stack/include/handlers.h b/bacnet-stack/include/handlers.h index 1aebaaa5..4bc6474a 100644 --- a/bacnet-stack/include/handlers.h +++ b/bacnet-stack/include/handlers.h @@ -136,6 +136,12 @@ extern "C" { BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data); + void handler_read_property_multiple_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); + /* Encodes the property APDU and returns the length, or sets the error, and returns -1 */ /* resides in h_rp.c */ diff --git a/bacnet-stack/include/memcopy.h b/bacnet-stack/include/memcopy.h new file mode 100644 index 00000000..00690e40 --- /dev/null +++ b/bacnet-stack/include/memcopy.h @@ -0,0 +1,60 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 by Steve Karg + + 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####*/ + +/* Functional Description: Memory copy function */ + +#ifndef MEMCOPY_H +#define MEMCOPY_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* copy len bytes from src to offset of dest if there is enough space. */ +/* returns 0 if there is not enough space, or the number of bytes copied. */ +size_t memcopy( + void * dest, + void * src, + size_t offset, /* where in dest to put the data */ + size_t len, /* amount of data to copy */ + size_t max); /* total size of destination */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/include/rpm.h b/bacnet-stack/include/rpm.h index e907db21..553d7d84 100644 --- a/bacnet-stack/include/rpm.h +++ b/bacnet-stack/include/rpm.h @@ -40,6 +40,16 @@ #include "bacdef.h" #include "bacapp.h" +struct BACnet_Read_Access_Data; +typedef struct BACnet_Read_Access_Data { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + /* simple linked list of values */ + BACNET_PROPERTY_REFERENCE *listOfProperties; + struct BACnet_Read_Access_Data *next; +} BACNET_READ_ACCESS_DATA; + + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -68,6 +78,12 @@ extern "C" { int rpm_encode_apdu_object_end( uint8_t * apdu); + int rpm_encode_apdu( + uint8_t * apdu, + size_t max_apdu, + uint8_t invoke_id, + BACNET_READ_ACCESS_DATA *read_access_data); + /* decode the object portion of the service request only */ int rpm_decode_object_id( uint8_t * apdu, @@ -129,7 +145,6 @@ extern "C" { unsigned apdu_len, BACNET_PROPERTY_ID * object_property, int32_t * array_index); - #ifdef TEST #include "ctest.h" int rpm_decode_apdu( diff --git a/bacnet-stack/include/sbuf.h b/bacnet-stack/include/sbuf.h index cab19197..5712ee27 100644 --- a/bacnet-stack/include/sbuf.h +++ b/bacnet-stack/include/sbuf.h @@ -55,32 +55,36 @@ extern "C" { void sbuf_init( STATIC_BUFFER * b, /* static buffer structure */ - char *data, /* actual size, in bytes, of the data block or array of data */ - unsigned size); /* number of bytes used */ -/* returns true if size==0, false if size > 0 */ + char *data, /* data block */ + unsigned size); /* actual size, in bytes, of the data block or array of data */ + + /* returns true if size==0, false if size > 0 */ bool sbuf_empty( STATIC_BUFFER const *b); + /* returns the data block, or NULL if not initialized */ char *sbuf_data( STATIC_BUFFER const *b); + /* returns the max size of the data block */ unsigned sbuf_size( STATIC_BUFFER * b); + /* returns the number of bytes used in the data block */ unsigned sbuf_count( STATIC_BUFFER * b); -/* returns true if successful, false if not enough room to append data */ + /* returns true if successful, false if not enough room to append data */ bool sbuf_put( STATIC_BUFFER * b, /* static buffer structure */ unsigned offset, /* where to start */ - char *data, /* number of bytes used */ + char *data, /* data to add */ unsigned data_size); /* how many to add */ -/* returns true if successful, false if not enough room to append data */ + /* returns true if successful, false if not enough room to append data */ bool sbuf_append( STATIC_BUFFER * b, /* static buffer structure */ - char *data, /* number of bytes used */ - unsigned data_size); /* how many to add */ -/* returns true if successful, false if not enough room to append data */ + char *data, /* data to append */ + unsigned data_size); /* how many to append */ + /* returns true if successful, false if count is bigger than size */ bool sbuf_truncate( STATIC_BUFFER * b, /* static buffer structure */ - unsigned count); /* total number of bytes in use */ + unsigned count); /* new number of bytes used in buffer */ #ifdef __cplusplus } diff --git a/bacnet-stack/lib/Makefile b/bacnet-stack/lib/Makefile index 00dbb462..944d7a94 100644 --- a/bacnet-stack/lib/Makefile +++ b/bacnet-stack/lib/Makefile @@ -65,6 +65,7 @@ CORE_SRC = \ $(BACNET_CORE)/abort.c \ $(BACNET_CORE)/reject.c \ $(BACNET_CORE)/bacerror.c \ + $(BACNET_CORE)/memcopy.c \ $(BACNET_CORE)/filename.c \ $(BACNET_CORE)/tsm.c \ $(BACNET_CORE)/bacaddr.c \ @@ -80,6 +81,7 @@ HANDLER_SRC = \ $(BACNET_HANDLER)/h_rp.c \ $(BACNET_HANDLER)/h_rp_a.c \ $(BACNET_HANDLER)/h_rpm.c \ + $(BACNET_HANDLER)/h_rpm_a.c \ $(BACNET_HANDLER)/h_wp.c \ $(BACNET_HANDLER)/h_arf.c \ $(BACNET_HANDLER)/h_arf_a.c \ @@ -98,6 +100,7 @@ HANDLER_SRC = \ $(BACNET_HANDLER)/s_iam.c \ $(BACNET_HANDLER)/s_rd.c \ $(BACNET_HANDLER)/s_rp.c \ + $(BACNET_HANDLER)/s_rpm.c \ $(BACNET_HANDLER)/s_ts.c \ $(BACNET_HANDLER)/s_whohas.c \ $(BACNET_HANDLER)/s_whois.c \ diff --git a/bacnet-stack/lib/bacnet.cbp b/bacnet-stack/lib/bacnet.cbp index 8f13ef4e..a2f2763e 100644 --- a/bacnet-stack/lib/bacnet.cbp +++ b/bacnet-stack/lib/bacnet.cbp @@ -82,6 +82,9 @@ + + @@ -118,9 +121,12 @@ - - + + + + @@ -228,7 +234,8 @@ - + + @@ -328,9 +335,12 @@ - - + + + + diff --git a/bacnet-stack/lib/bacnetdll.cbp b/bacnet-stack/lib/bacnetdll.cbp index 55035b6d..b7c48f0d 100644 --- a/bacnet-stack/lib/bacnetdll.cbp +++ b/bacnet-stack/lib/bacnetdll.cbp @@ -209,9 +209,12 @@ - - + + + + diff --git a/bacnet-stack/lib/makefile.b32 b/bacnet-stack/lib/makefile.b32 index fee8c8e9..7d733ee6 100644 --- a/bacnet-stack/lib/makefile.b32 +++ b/bacnet-stack/lib/makefile.b32 @@ -51,6 +51,7 @@ CORE1_SRC = $(BACNET_CORE)\apdu.c \ $(BACNET_CORE)\reject.c \ $(BACNET_CORE)\bacerror.c \ $(BACNET_CORE)\filename.c \ + $(BACNET_CORE)\memcopy.c \ $(BACNET_CORE)\tsm.c \ $(BACNET_CORE)\bacaddr.c \ $(BACNET_CORE)\address.c \ @@ -78,6 +79,7 @@ HANDLER_SRC = $(BACNET_HANDLER)\txbuf.c \ $(BACNET_HANDLER)\h_rp.c \ $(BACNET_HANDLER)\h_rp_a.c \ $(BACNET_HANDLER)\h_rpm.c \ + $(BACNET_HANDLER)\h_rpm_a.c \ $(BACNET_HANDLER)\h_wp.c \ $(BACNET_HANDLER)\h_arf.c \ $(BACNET_HANDLER)\h_arf_a.c \ @@ -96,6 +98,7 @@ HANDLER_SRC = $(BACNET_HANDLER)\txbuf.c \ $(BACNET_HANDLER)\s_iam.c \ $(BACNET_HANDLER)\s_rd.c \ $(BACNET_HANDLER)\s_rp.c \ + $(BACNET_HANDLER)\s_rpm.c \ $(BACNET_HANDLER)\s_ts.c \ $(BACNET_HANDLER)\s_whohas.c \ $(BACNET_HANDLER)\s_whois.c \ diff --git a/bacnet-stack/makefile.b32 b/bacnet-stack/makefile.b32 index b001da26..146f54f4 100644 --- a/bacnet-stack/makefile.b32 +++ b/bacnet-stack/makefile.b32 @@ -12,11 +12,12 @@ MAKE=$(BORLAND_DIR)\bin\make.exe all: library \ readprop writeprop readfile writefile server dcc reinit \ - whois whohas timesync ucov epics + whois whohas timesync ucov epics readpropm @echo "demo utilities are in the bin directory" clean: lib\makefile.b32 \ demo/readprop/makefile.b32 \ + demo/readpropm/makefile.b32 \ demo/writeprop/makefile.b32 \ demo/readfile/makefile.b32 \ demo/writefile/makefile.b32 \ @@ -35,6 +36,10 @@ clean: lib\makefile.b32 \ $(MAKE) -i -f makefile.b32 clean cd .. cd .. + cd demo/readpropm + $(MAKE) -i -f makefile.b32 clean + cd .. + cd .. cd demo/writeprop $(MAKE) -i -f makefile.b32 clean cd .. @@ -92,6 +97,13 @@ readprop: demo/readprop/makefile.b32 cd .. cd .. +readpropm: demo/readpropm/makefile.b32 + cd demo/readpropm + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + writeprop: demo/writeprop/makefile.b32 cd demo/writeprop $(MAKE) -f makefile.b32 all diff --git a/bacnet-stack/src/bacdcode.c b/bacnet-stack/src/bacdcode.c index 1344148b..ea7a64e0 100644 --- a/bacnet-stack/src/bacdcode.c +++ b/bacnet-stack/src/bacdcode.c @@ -439,7 +439,7 @@ bool decode_is_context_tag_with_length( } /* from clause 20.2.1.3.2 Constructed Data */ -/* returns the number of apdu bytes consumed */ +/* returns the true if the tag matches */ bool decode_is_opening_tag_number( uint8_t * apdu, uint8_t tag_number) diff --git a/bacnet-stack/src/memcopy.c b/bacnet-stack/src/memcopy.c new file mode 100644 index 00000000..b3613c7f --- /dev/null +++ b/bacnet-stack/src/memcopy.c @@ -0,0 +1,110 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 Steve Karg + + 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 + +/* copy len bytes from src to offset of dest if there is enough space. */ +/* returns 0 if there is not enough space, or the number of bytes copied. */ +size_t memcopy( + void * dest, + void * src, + size_t offset, /* where in dest to put the data */ + size_t len, /* amount of data to copy */ + size_t max) /* total size of destination */ +{ + size_t i; + size_t copy_len = 0; + char *s1, *s2; + + s1 = dest; + s2 = src; + if (len <= (max - offset)) { + for (i = 0; i < len; i++) { + s1[offset + i] = s2[i]; + copy_len++; + } + } + + return copy_len; +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +void test_memcopy( + Test * pTest) +{ + char *data1 = "Joshua"; + char *data2 = "Anna"; + char buffer[480] = ""; + char big_buffer[480] = ""; + size_t len = 0; + + len = memcopy(&buffer[0], &data1[0], 0, + sizeof(data1), sizeof(buffer)); + ct_test(pTest, len == sizeof(data1)); + ct_test(pTest, memcmp(&buffer[0], &data1[0], len) == 0); + len = memcopy(&buffer[0], &data2[0], len, + sizeof(data2), sizeof(buffer)); + ct_test(pTest, len == sizeof(data2)); + len = memcopy(&buffer[0], &big_buffer[0], 1, + sizeof(big_buffer), sizeof(buffer)); + ct_test(pTest, len == 0); +} + +#ifdef TEST_MEM_COPY +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("Memory Copy", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, test_memcopy); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif +#endif /* TEST */ diff --git a/bacnet-stack/src/rpm.c b/bacnet-stack/src/rpm.c index 285644da..e5d8a0ba 100644 --- a/bacnet-stack/src/rpm.c +++ b/bacnet-stack/src/rpm.c @@ -37,6 +37,7 @@ #include "bacdcode.h" #include "bacdef.h" #include "bacapp.h" +#include "memcopy.h" #include "rpm.h" /* encode the initial portion of the service */ @@ -105,6 +106,75 @@ int rpm_encode_apdu_object_end( return apdu_len; } +int rpm_encode_apdu( + uint8_t * apdu, + size_t max_apdu, + uint8_t invoke_id, + BACNET_READ_ACCESS_DATA *read_access_data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + int len = 0; /* length of the data */ + BACNET_READ_ACCESS_DATA *rpm_object; /* current object */ + uint8_t apdu_temp[16]; /* temp for data before copy */ + BACNET_PROPERTY_REFERENCE *rpm_property; /* current property */ + + len = rpm_encode_apdu_init(&apdu_temp[0], invoke_id); + len = memcopy(&apdu[0], &apdu_temp[0], apdu_len, len, max_apdu); + if (len == 0) { + return 0; + } + apdu_len += len; + rpm_object = read_access_data; + while (rpm_object) { + len = encode_context_object_id(&apdu_temp[0], 0, + rpm_object->object_type, + rpm_object->object_instance); + len = memcopy(&apdu[0], &apdu_temp[0], apdu_len, len, max_apdu); + if (len == 0) { + return 0; + } + apdu_len += len; + /* Tag 1: sequence of ReadAccessSpecification */ + len = encode_opening_tag(&apdu_temp[0], 1); + len = memcopy(&apdu[0], &apdu_temp[0], apdu_len, len, max_apdu); + if (len == 0) { + return 0; + } + apdu_len += len; + rpm_property = rpm_object->listOfProperties; + while (rpm_property) { + /* stuff as many properties into it as APDU length will allow */ + len = encode_context_enumerated(&apdu_temp[0], 0, + rpm_property->propertyIdentifier); + len = memcopy(&apdu[0], &apdu_temp[0], apdu_len, len, max_apdu); + if (len == 0) { + return 0; + } + apdu_len += len; + /* optional array index */ + if (rpm_property->propertyArrayIndex != BACNET_ARRAY_ALL) { + len = encode_context_unsigned(&apdu_temp[0], 1, + rpm_property->propertyArrayIndex); + len = memcopy(&apdu[0], &apdu_temp[0], apdu_len, len, max_apdu); + if (len == 0) { + return 0; + } + apdu_len += len; + } + rpm_property = rpm_property->next; + } + len = encode_closing_tag(&apdu_temp[0], 1); + len = memcopy(&apdu[0], &apdu_temp[0], apdu_len, len, max_apdu); + if (len == 0) { + return 0; + } + apdu_len += len; + rpm_object = rpm_object->next; + } + + return apdu_len; +} + /* decode the object portion of the service request only */ int rpm_decode_object_id( uint8_t * apdu, diff --git a/bacnet-stack/src/sbuf.c b/bacnet-stack/src/sbuf.c index 19174e7a..24a38be8 100644 --- a/bacnet-stack/src/sbuf.c +++ b/bacnet-stack/src/sbuf.c @@ -42,9 +42,9 @@ void sbuf_init( STATIC_BUFFER * b, /* static buffer structure */ - char *data, /* actual size, in bytes, of the data block or array of data */ - unsigned size) -{ /* number of bytes used */ + char *data, /* data block */ + unsigned size) /* actual size, in bytes, of the data block or array of data */ +{ if (b) { b->data = data; b->size = size; @@ -83,9 +83,9 @@ unsigned sbuf_count( bool sbuf_put( STATIC_BUFFER * b, /* static buffer structure */ unsigned offset, /* where to start */ - char *data, /* number of bytes used */ + char *data, /* data to place in buffer */ unsigned data_size) -{ /* how many to add */ +{ /* how many bytes to add */ bool status = false; /* return value */ if (b && b->data) { @@ -107,13 +107,14 @@ bool sbuf_put( /* returns true if successful, false if not enough room to append data */ bool sbuf_append( STATIC_BUFFER * b, /* static buffer structure */ - char *data, /* number of bytes used */ + char *data, /* data to place in buffer */ unsigned data_size) -{ /* how many to add */ +{ /* how many bytes to add */ unsigned count = 0; - if (b) + if (b) { count = b->count; + } return sbuf_put(b, count, data, data_size); } @@ -122,7 +123,7 @@ bool sbuf_append( bool sbuf_truncate( STATIC_BUFFER * b, /* static buffer structure */ unsigned count) -{ /* total number of bytes in use */ +{ /* total number of bytes in to remove */ bool status = false; /* return value */ if (b) { diff --git a/bacnet-stack/test.mak b/bacnet-stack/test.mak index 452a9388..a50ddb86 100644 --- a/bacnet-stack/test.mak +++ b/bacnet-stack/test.mak @@ -131,6 +131,12 @@ mstp: logfile test/mstp.mak ( ./test/mstp >> ${LOGFILE} ) ( cd test ; make -f mstp.mak clean ) +memcopy: logfile test/memcopy.mak + ( cd test ; make -f memcopy.mak clean ) + ( cd test ; make -f memcopy.mak ) + ( ./test/memcopy >> ${LOGFILE} ) + ( cd test ; make -f memcopy.mak clean ) + npdu: logfile test/npdu.mak ( cd test ; make -f npdu.mak clean ) ( cd test ; make -f npdu.mak ) diff --git a/bacnet-stack/test/memcopy.mak b/bacnet-stack/test/memcopy.mak new file mode 100644 index 00000000..3f336711 --- /dev/null +++ b/bacnet-stack/test/memcopy.mak @@ -0,0 +1,32 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_MEM_COPY + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/memcopy.c \ + ctest.c + +TARGET = memcopy + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend +