From 23807e0c8335ffaa56b4cc0b950492629aceb772 Mon Sep 17 00:00:00 2001 From: skarg Date: Tue, 3 Apr 2007 11:04:25 +0000 Subject: [PATCH] Enhancing the EPICS demo. --- bacnet-stack/apdu.h | 38 +- bacnet-stack/demo/epics/Makefile | 4 +- bacnet-stack/demo/epics/main.c | 103 +++- bacnet-stack/demo/epics/makefile.b32 | 3 +- bacnet-stack/key.c | 116 +++++ bacnet-stack/key.h | 60 +++ bacnet-stack/keylist.c | 716 +++++++++++++++++++++++++++ bacnet-stack/keylist.h | 93 ++++ bacnet-stack/keylist.mak | 38 ++ 9 files changed, 1140 insertions(+), 31 deletions(-) create mode 100644 bacnet-stack/key.c create mode 100644 bacnet-stack/key.h create mode 100644 bacnet-stack/keylist.c create mode 100644 bacnet-stack/keylist.h create mode 100644 bacnet-stack/keylist.mak diff --git a/bacnet-stack/apdu.h b/bacnet-stack/apdu.h index 6bf48bde..b37d46a7 100644 --- a/bacnet-stack/apdu.h +++ b/bacnet-stack/apdu.h @@ -39,29 +39,29 @@ #include "bacdef.h" #include "bacenum.h" +typedef struct _confirmed_service_data { + bool segmented_message; + bool more_follows; + bool segmented_response_accepted; + int max_segs; + int max_resp; + uint8_t invoke_id; + uint8_t sequence_number; + uint8_t proposed_window_number; +} BACNET_CONFIRMED_SERVICE_DATA; + +typedef struct _confirmed_service_ack_data { + bool segmented_message; + bool more_follows; + uint8_t invoke_id; + uint8_t sequence_number; + uint8_t proposed_window_number; +} BACNET_CONFIRMED_SERVICE_ACK_DATA; + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ - typedef struct _confirmed_service_data { - bool segmented_message; - bool more_follows; - bool segmented_response_accepted; - int max_segs; - int max_resp; - uint8_t invoke_id; - uint8_t sequence_number; - uint8_t proposed_window_number; - } BACNET_CONFIRMED_SERVICE_DATA; - - typedef struct _confirmed_service_ack_data { - bool segmented_message; - bool more_follows; - uint8_t invoke_id; - uint8_t sequence_number; - uint8_t proposed_window_number; - } BACNET_CONFIRMED_SERVICE_ACK_DATA; - /* generic unconfirmed function handler */ /* Suitable to handle the following services: */ /* I_Am, Who_Is, Unconfirmed_COV_Notification, I_Have, */ diff --git a/bacnet-stack/demo/epics/Makefile b/bacnet-stack/demo/epics/Makefile index 6d971a49..8e68d486 100644 --- a/bacnet-stack/demo/epics/Makefile +++ b/bacnet-stack/demo/epics/Makefile @@ -25,12 +25,13 @@ SRCS = main.c \ $(BACNET_PORT)/ethernet.c \ $(BACNET_PORT)/arcnet.c \ $(BACNET_ROOT)/bip.c \ + $(BACNET_ROOT)/key.c \ + $(BACNET_ROOT)/keylist.c \ $(BACNET_HANDLER)/txbuf.c \ $(BACNET_HANDLER)/noserv.c \ $(BACNET_HANDLER)/h_whois.c \ $(BACNET_HANDLER)/h_rp.c \ $(BACNET_HANDLER)/h_iam.c \ - $(BACNET_HANDLER)/h_rp_a.c \ $(BACNET_HANDLER)/s_rp.c \ $(BACNET_HANDLER)/s_whois.c \ $(BACNET_OBJECT)/device.c \ @@ -84,4 +85,3 @@ clean: rm -rf core ${TARGET} $(OBJS) *.bak ports/linux/*.bak *.1 *.ini include: .depend - diff --git a/bacnet-stack/demo/epics/main.c b/bacnet-stack/demo/epics/main.c index d8dee3bd..7065b064 100644 --- a/bacnet-stack/demo/epics/main.c +++ b/bacnet-stack/demo/epics/main.c @@ -43,11 +43,13 @@ #include "net.h" #include "datalink.h" #include "whois.h" +#include "rp.h" /* some demo stuff needed */ #include "filename.h" #include "handlers.h" #include "client.h" #include "txbuf.h" +#include "keylist.h" /* buffer used for receive */ static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; @@ -57,6 +59,15 @@ static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; static bool Error_Detected = false; static BACNET_ADDRESS Target_Address; +typedef struct BACnet_RP_Service_Data_t { + bool new_data; + BACNET_CONFIRMED_SERVICE_ACK_DATA service_data; + BACNET_READ_PROPERTY_DATA data; +} BACNET_RP_SERVICE_DATA; +static BACNET_RP_SERVICE_DATA Read_Property_Data; + +static OS_Keylist Object_List; + static void MyErrorHandler(BACNET_ADDRESS * src, uint8_t invoke_id, BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code) @@ -64,7 +75,7 @@ static void MyErrorHandler(BACNET_ADDRESS * src, /* FIXME: verify src and invoke id */ (void) src; (void) invoke_id; - #if 0 + #if 1 printf("BACnet Error: %s: %s\r\n", bactext_error_class_name(error_class), bactext_error_code_name(error_code)); @@ -82,7 +93,7 @@ void MyAbortHandler(BACNET_ADDRESS * src, (void) src; (void) invoke_id; (void) server; - #if 0 + #if 1 printf("BACnet Abort: %s\r\n", bactext_abort_reason_name(abort_reason)); #else @@ -97,7 +108,7 @@ void MyRejectHandler(BACNET_ADDRESS * src, /* FIXME: verify src and invoke id */ (void) src; (void) invoke_id; - #if 0 + #if 1 printf("BACnet Reject: %s\r\n", bactext_reject_reason_name(reject_reason)); #else @@ -106,6 +117,75 @@ void MyRejectHandler(BACNET_ADDRESS * src, Error_Detected = true; } +void PrintReadPropertyData(BACNET_READ_PROPERTY_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) { +#if 0 + if (data->array_index == BACNET_ARRAY_ALL) + fprintf(stderr, "%s #%u %s\n", + bactext_object_type_name(data->object_type), + data->object_instance, + bactext_property_name(data->object_property)); + else + fprintf(stderr, "%s #%u %s[%d]\n", + bactext_object_type_name(data->object_type), + data->object_instance, + bactext_property_name(data->object_property), + data->array_index); +#endif + application_data = data->application_data; + application_data_len = data->application_data_len; + /* value? 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; + fprintf(stdout, "{"); + 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! */ + fprintf(stdout, ","); + } else + break; + } else + break; + } + if (print_brace) + fprintf(stdout, "}"); + fprintf(stdout, "\r\n"); + } +} + +void MyReadPropertyAckHandler(uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_READ_PROPERTY_DATA data; + + (void) src; + len = rp_ack_decode_service_request(service_request, + service_len, &data); + if (len > 0) { + memmove(&Read_Property_Data.service_data, service_data, sizeof(data)); + memmove(&Read_Property_Data.data, &data, sizeof(data)); + Read_Property_Data.new_data = true; + } +} + static void Init_Service_Handlers(void) { /* we need to handle who-is @@ -124,7 +204,7 @@ static void Init_Service_Handlers(void) handler_read_property); /* handle the data coming back from confirmed requests */ apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, - handler_read_property_ack); + MyReadPropertyAckHandler); /* handle any errors coming back */ apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); @@ -170,14 +250,12 @@ static uint8_t Read_Properties(uint32_t device_instance) /* note: PROP_OBJECT_LIST is missing cause we need to get it with an index method since the list could be very large */ - /* some proprietary properties */ - 514, 515, /* end of list */ -1 }; if (object_props[index] != -1) { - printf("%s: ",bactext_property_name(object_props[index])); + printf(" %s: ",bactext_property_name(object_props[index])); invoke_id = Send_Read_Property_Request(device_instance, OBJECT_DEVICE, device_instance, @@ -229,7 +307,7 @@ int main(int argc, char *argv[]) bip_set_interface("eth0"); if (!bip_init()) return 1; - printf("bip: using port %hu\r\n", bip_get_port()); + /* printf("bip: using port %hu\r\n", bip_get_port()); */ #elif defined(BACDL_ARCNET) if (!arcnet_init("arc0")) return 1; @@ -243,6 +321,8 @@ int main(int argc, char *argv[]) /* try to bind with the device */ Send_WhoIs(Target_Device_Object_Instance, Target_Device_Object_Instance); + printf("List of Objects in test device:\r\n"); + printf("{\r\n"); /* loop forever */ for (;;) { /* increment timer - exit if timed out */ @@ -268,6 +348,10 @@ int main(int argc, char *argv[]) if (invoke_id == 0) { break; } + } else if ((Read_Property_Data.new_data) && + (invoke_id == Read_Property_Data.service_data.invoke_id)) { + Read_Property_Data.new_data = false; + PrintReadPropertyData(&Read_Property_Data.data); } else if (tsm_invoke_id_free(invoke_id)) { invoke_id = 0; } @@ -277,7 +361,7 @@ int main(int argc, char *argv[]) invoke_id = 0; } else if (Error_Detected) { invoke_id = 0; - } + } } else { /* increment timer - exit if timed out */ elapsed_seconds += (current_seconds - last_seconds); @@ -289,6 +373,7 @@ int main(int argc, char *argv[]) /* keep track of time for next check */ last_seconds = current_seconds; } + printf("}\r\n"); return 0; } diff --git a/bacnet-stack/demo/epics/makefile.b32 b/bacnet-stack/demo/epics/makefile.b32 index 95191f9f..183e97da 100644 --- a/bacnet-stack/demo/epics/makefile.b32 +++ b/bacnet-stack/demo/epics/makefile.b32 @@ -21,12 +21,13 @@ SRCS = main.c \ ..\..\ports\win32\bip-init.c \ ..\..\filename.c \ ..\..\bip.c \ + ..\..\key.c \ + ..\..\keylist.c \ ..\..\demo\handler\txbuf.c \ ..\..\demo\handler\noserv.c \ ..\..\demo\handler\h_whois.c \ ..\..\demo\handler\h_iam.c \ ..\..\demo\handler\h_rp.c \ - ..\..\demo\handler\h_rp_a.c \ ..\..\demo\handler\s_rp.c \ ..\..\demo\handler\s_whois.c \ ..\..\bacdcode.c \ diff --git a/bacnet-stack/key.c b/bacnet-stack/key.c new file mode 100644 index 00000000..306eff5a --- /dev/null +++ b/bacnet-stack/key.c @@ -0,0 +1,116 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 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####*/ +//#define TEST +//#define TEST_KEY +#include "key.h" + +#ifdef TEST +#include +#include + +#include "ctest.h" + +// test the encode and decode macros +void testKeys(Test * pTest) +{ + int type, id; + int decoded_type, decoded_id; + KEY key; + + for (type = 0; type < KEY_TYPE_MAX; type++) { + for (id = 0; id < KEY_ID_MAX; id++) { + key = KEY_ENCODE(type, id); + decoded_type = KEY_DECODE_TYPE(key); + decoded_id = KEY_DECODE_ID(key); + ct_test(pTest, decoded_type == type); + ct_test(pTest, decoded_id == id); + } + } + + return; +} + +// test the encode and decode macros +void testKeySample(Test * pTest) +{ + int type, id; + int type_list[] = { 0, 1, KEY_TYPE_MAX / 2, KEY_TYPE_MAX - 1, -1 }; + int id_list[] = { 0, 1, KEY_ID_MAX / 2, KEY_ID_MAX - 1, -1 }; + int type_index = 0; + int id_index = 0; + int decoded_type, decoded_id; + KEY key; + + while (type_list[type_index] != -1) { + while (id_list[id_index] != -1) { + type = type_list[type_index]; + id = id_list[id_index]; + key = KEY_ENCODE(type, id); + decoded_type = KEY_DECODE_TYPE(key); + decoded_id = KEY_DECODE_ID(key); + ct_test(pTest, decoded_type == type); + ct_test(pTest, decoded_id == id); + + id_index++; + } + id_index = 0; + type_index++; + } + + return; +} + +#ifdef TEST_KEY +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("key", NULL); + /* add the individual tests */ +// rc = ct_addTestFunction(pTest, testKeys); +// assert(rc); + rc = ct_addTestFunction(pTest, testKeySample); + assert(rc); + /* run all the tests */ + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + /* completed testing - cleanup */ + ct_destroy(pTest); + + return 0; +} +#endif /* LOCAL_TEST */ +#endif diff --git a/bacnet-stack/key.h b/bacnet-stack/key.h new file mode 100644 index 00000000..1bb66000 --- /dev/null +++ b/bacnet-stack/key.h @@ -0,0 +1,60 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 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####*/ +#ifndef KEY_H +#define KEY_H + +#include + +// This file has the macros that encode and decode the +// keys for the keylist when used with BACnet Object Id's +typedef uint32_t KEY; + +// assuming a 32 bit KEY +#define KEY_TYPE_OFFSET 22 /* bits - for BACnet */ +#define KEY_TYPE_MASK 0x000003FFL +#define KEY_ID_MASK 0x003FFFFFL +#define KEY_ID_MAX (KEY_ID_MASK + 1L) +#define KEY_TYPE_MAX (KEY_TYPE_MASK + 1L) +#define KEY_LAST(key) ((key & KEY_ID_MASK) == KEY_ID_MAX) + +#define KEY_ENCODE(type,id) ( ((unsigned int)\ +((unsigned int)(type) & KEY_TYPE_MASK) << KEY_TYPE_OFFSET) |\ + ((unsigned int)(id) & KEY_ID_MASK) ) + +#define KEY_DECODE_TYPE(key) ((int)(((unsigned int)(key) >> KEY_TYPE_OFFSET)\ + & KEY_TYPE_MASK)) + +#define KEY_DECODE_ID(key) ((int)((unsigned int)(key) & KEY_ID_MASK)) + +#endif diff --git a/bacnet-stack/keylist.c b/bacnet-stack/keylist.c new file mode 100644 index 00000000..c34293d3 --- /dev/null +++ b/bacnet-stack/keylist.c @@ -0,0 +1,716 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 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####*/ + +// Keyed Linked List Library +// +// This is an enhanced array of pointers to data. +// The list is sorted, indexed, and keyed. +// The array is much faster than a linked list. +// It stores a pointer to data, which you must +// malloc and free on your own, or just use +// static data + +#include + +#include "keylist.h" // check for valid prototypes + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +///////////////////////////////////////////////////////////////////// +// Generic node routines +///////////////////////////////////////////////////////////////////// + +// grab memory for a node +static struct Keylist_Node *NodeCreate(void) +{ + return calloc(1, sizeof(struct Keylist_Node)); +} + +// grab memory for a list +static struct Keylist *KeylistCreate(void) +{ + return calloc(1, sizeof(struct Keylist)); +} + +// check to see if the array is big enough for an addition +// or is too big when we are deleting and we can shrink +// returns TRUE if success, FALSE if failed +static int CheckArraySize(OS_Keylist list) +{ + int new_size = 0; // set it up so that no size change is the default + const int chunk = 8; // minimum number of nodes to allocate memory for + struct Keylist_Node **new_array; // new array of nodes, if needed + int i; // counter + if (!list) + return FALSE; + + // indicates the need for more memory allocation + if (list->count == list->size) + new_size = list->size + chunk; + + // allow for shrinking memory + else if ((list->size > chunk) && (list->count < (list->size - chunk))) + new_size = list->size - chunk; + if (new_size) { + + // Allocate more room for node pointer array + new_array = calloc((size_t) new_size, sizeof(new_array)); + + // See if we got the memory we wanted + if (!new_array) + return FALSE; + + // copy the nodes from the old array to the new array + if (list->array) { + for (i = 0; i < list->count; i++) { + new_array[i] = list->array[i]; + } + free(list->array); + } + list->array = new_array; + list->size = new_size; + } + return TRUE; +} + + +// find the index of the key that we are looking for +// since it is sorted, we can optimize the search +// returns TRUE if found, and FALSE not found +// returns the found key and the index where it was found in parameters +// If the key is not found, the nearest index from the bottom will be returned, +// allowing the ability to find where an key should go into the list. +static int FindIndex(OS_Keylist list, KEY key, int *pIndex) +{ + struct Keylist_Node *node; // holds the new node + int left = 0; // the left branch of tree, beginning of list + int right = 0; // the right branch on the tree, end of list + int index = 0; // our current search place in the array + KEY current_key = 0; // place holder for current node key + int status = FALSE; // return value + if (!list || !list->array || !list->count) { + *pIndex = 0; + return (FALSE); + } + right = list->count - 1; + // assume that the list is sorted + do { + + // A binary search + index = (left + right) / 2; + node = list->array[index]; + if (!node) + break; + current_key = node->key; + if (key < current_key) + right = index - 1; + + else + left = index + 1; + } + while ((key != current_key) && (left <= right)); + if (key == current_key) { + status = TRUE; + *pIndex = index; + } + + else { + + // where the index should be + if (key > current_key) + *pIndex = index + 1; + + else + *pIndex = index; + } + return (status); +} + + +///////////////////////////////////////////////////////////////////// +// list data functions +///////////////////////////////////////////////////////////////////// +// inserts a node into its sorted position +int Keylist_Data_Add(OS_Keylist list, KEY key, void *data) +{ + struct Keylist_Node *node; // holds the new node + int index = -1; // return value + int i; // counts through the array + + if (list && CheckArraySize(list)) { + // figure out where to put the new node + if (list->count) { + (void) FindIndex(list, key, &index); + // Add to the beginning of the list + if (index < 0) + index = 0; + + // Add to the end of the list + else if (index > list->count) + index = list->count; + + // Move all the items up to make room for the new one + for (i = list->count; i > index; i--) { + list->array[i] = list->array[i - 1]; + } + } + + else { + index = 0; + } + + // create and add the node + node = NodeCreate(); + if (node) { + list->count++; + node->key = key; + node->data = data; + list->array[index] = node; + } + } + return index; +} + +// deletes a node specified by its index +// returns the data from the node +void *Keylist_Data_Delete_By_Index(OS_Keylist list, int index) +{ + struct Keylist_Node *node; + void *data = NULL; + + if (list && list->array && list->count && + (index >= 0) && (index < list->count)) { + node = list->array[index]; + if (node) + data = node->data; + + // move the nodes to account for the deleted one + if (list->count == 1) { + + // There is no node shifting to do + } + // We are the last one + else if (index == (list->count - 1)) { + + // There is no node shifting to do + } + // Move all the nodes down one + else { + int i; // counter + int count = list->count - 1; + for (i = index; i < count; i++) { + list->array[i] = list->array[i + 1]; + } + } + list->count--; + if (node) + free(node); + + // potentially reduce the size of the array + (void) CheckArraySize(list); + } + return (data); +} + + +// deletes a node specified by its key +// returns the data from the node +void *Keylist_Data_Delete(OS_Keylist list, KEY key) +{ + void *data = NULL; // return value + int index; // where the node is in the array + + if (list) { + if (FindIndex(list, key, &index)) + data = Keylist_Data_Delete_By_Index(list, index); + } + + return data; +} + +// returns the data from last node, and removes it from the list +void *Keylist_Data_Pop(OS_Keylist list) +{ + void *data = NULL; // return value + int index; // position in the array + + if (list && list->count) { + index = list->count - 1; + data = Keylist_Data_Delete_By_Index(list, index); + } + + return data; +} + +// returns the data from the node specified by key +void *Keylist_Data(OS_Keylist list, KEY key) +{ + struct Keylist_Node *node = NULL; + int index = 0; // used to look up the index of node + + if (list && list->array && list->count) { + if (FindIndex(list, key, &index)) + node = list->array[index]; + } + + return node ? node->data : NULL; +} + +// returns the data specified by key +void *Keylist_Data_Index(OS_Keylist list, int index) +{ + struct Keylist_Node *node = NULL; + + if (list && list->array && list->count && + (index >= 0) && (index < list->count)) + node = list->array[index]; + + return node ? node->data : NULL; +} + +// return the key at the given index +KEY Keylist_Key(OS_Keylist list, int index) +{ + KEY key = 0; // return value + struct Keylist_Node *node; + + if (list && list->array && list->count && + (index >= 0) && (index < list->count)) { + node = list->array[index]; + if (node) + key = node->key; + } + + return key; +} + +// returns the next empty key from the list +KEY Keylist_Next_Empty_Key(OS_Keylist list, KEY key) +{ + int index; + + if (list) { + while (FindIndex(list, key, &index)) { + if (KEY_LAST(key)) + break; + key++; + } + } + + return key; +} + +// return the number of nodes in this list +int Keylist_Count(OS_Keylist list) +{ + return list->count; +} + +///////////////////////////////////////////////////////////////////// +// Public List functions +///////////////////////////////////////////////////////////////////// + +// returns head of the list or NULL on failure. +OS_Keylist Keylist_Create(void) +{ + struct Keylist *list; + + list = KeylistCreate(); + if (list) + CheckArraySize(list); + + return list; +} + +// delete specified list +void Keylist_Delete(OS_Keylist list) // list number to be deleted +{ + if (list) { + // clean out the list + while (list->count) { + (void) Keylist_Data_Delete_By_Index(list, 0); + } + if (list->array) + free(list->array); + free(list); + } + + return; +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +// test the encode and decode macros +void testKeySample(Test * pTest) +{ + int type, id; + int type_list[] = + { 0, 1, KEY_TYPE_MAX / 2, KEY_TYPE_MAX - 2, KEY_TYPE_MAX - 1, -1 }; + int id_list[] = + { 0, 1, KEY_ID_MAX / 2, KEY_ID_MAX - 2, KEY_ID_MAX - 1, -1 }; + int type_index = 0; + int id_index = 0; + int decoded_type, decoded_id; + KEY key; + + while (type_list[type_index] != -1) { + while (id_list[id_index] != -1) { + type = type_list[type_index]; + id = id_list[id_index]; + key = KEY_ENCODE(type, id); + decoded_type = KEY_DECODE_TYPE(key); + decoded_id = KEY_DECODE_ID(key); + ct_test(pTest, decoded_type == type); + ct_test(pTest, decoded_id == id); + + id_index++; + } + id_index = 0; + type_index++; + } + + return; +} + +// test the FIFO +void testKeyListFIFO(Test * pTest) +{ + OS_Keylist list; + KEY key; + int index; + char *data1 = "Joshua"; + char *data2 = "Anna"; + char *data3 = "Mary"; + char *data; + + list = Keylist_Create(); + ct_test(pTest, list != NULL); + + key = 0; + index = Keylist_Data_Add(list, key, data1); + ct_test(pTest, index == 0); + index = Keylist_Data_Add(list, key, data2); + ct_test(pTest, index == 0); + index = Keylist_Data_Add(list, key, data3); + ct_test(pTest, index == 0); + + ct_test(pTest, Keylist_Count(list) == 3); + + data = Keylist_Data_Pop(list); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + data = Keylist_Data_Pop(list); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data2) == 0); + data = Keylist_Data_Pop(list); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data3) == 0); + data = Keylist_Data_Pop(list); + ct_test(pTest, data == NULL); + data = Keylist_Data_Pop(list); + ct_test(pTest, data == NULL); + + Keylist_Delete(list); + + return; +} + +// test the FILO +void testKeyListFILO(Test * pTest) +{ + OS_Keylist list; + KEY key; + int index; + char *data1 = "Joshua"; + char *data2 = "Anna"; + char *data3 = "Mary"; + char *data; + + list = Keylist_Create(); + ct_test(pTest, list != NULL); + + key = 0; + index = Keylist_Data_Add(list, key, data1); + ct_test(pTest, index == 0); + index = Keylist_Data_Add(list, key, data2); + ct_test(pTest, index == 0); + index = Keylist_Data_Add(list, key, data3); + ct_test(pTest, index == 0); + + ct_test(pTest, Keylist_Count(list) == 3); + + data = Keylist_Data_Delete_By_Index(list, 0); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data3) == 0); + + data = Keylist_Data_Delete_By_Index(list, 0); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data2) == 0); + + data = Keylist_Data_Delete_By_Index(list, 0); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + + data = Keylist_Data_Delete_By_Index(list, 0); + ct_test(pTest, data == NULL); + + data = Keylist_Data_Delete_By_Index(list, 0); + ct_test(pTest, data == NULL); + + Keylist_Delete(list); + + return; +} + +void testKeyListDataKey(Test * pTest) +{ + OS_Keylist list; + KEY key; + KEY test_key; + int index; + char *data1 = "Joshua"; + char *data2 = "Anna"; + char *data3 = "Mary"; + char *data; + + list = Keylist_Create(); + ct_test(pTest, list != NULL); + + key = 1; + index = Keylist_Data_Add(list, key, data1); + ct_test(pTest, index == 0); + test_key = Keylist_Key(list, index); + ct_test(pTest, test_key == key); + + key = 2; + index = Keylist_Data_Add(list, key, data2); + ct_test(pTest, index == 1); + test_key = Keylist_Key(list, index); + ct_test(pTest, test_key == key); + + key = 3; + index = Keylist_Data_Add(list, key, data3); + ct_test(pTest, index == 2); + test_key = Keylist_Key(list, index); + ct_test(pTest, test_key == key); + + ct_test(pTest, Keylist_Count(list) == 3); + + // look at the data + key = 2; + data = Keylist_Data(list, key); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data2) == 0); + + key = 1; + data = Keylist_Data(list, key); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + + key = 3; + data = Keylist_Data(list, key); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data3) == 0); + + // work the data + key = 2; + data = Keylist_Data_Delete(list, key); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data2) == 0); + data = Keylist_Data_Delete(list, key); + ct_test(pTest, data == NULL); + ct_test(pTest, Keylist_Count(list) == 2); + + key = 1; + data = Keylist_Data(list, key); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + + key = 3; + data = Keylist_Data(list, key); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data3) == 0); + + // cleanup + do { + data = Keylist_Data_Pop(list); + } + while (data); + + Keylist_Delete(list); + + return; +} + +void testKeyListDataIndex(Test * pTest) +{ + OS_Keylist list; + KEY key; + int index; + char *data1 = "Joshua"; + char *data2 = "Anna"; + char *data3 = "Mary"; + char *data; + + list = Keylist_Create(); + ct_test(pTest, list != NULL); + + key = 0; + index = Keylist_Data_Add(list, key, data1); + ct_test(pTest, index == 0); + index = Keylist_Data_Add(list, key, data2); + ct_test(pTest, index == 0); + index = Keylist_Data_Add(list, key, data3); + ct_test(pTest, index == 0); + + + ct_test(pTest, Keylist_Count(list) == 3); + + // look at the data + data = Keylist_Data_Index(list, 0); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data3) == 0); + + data = Keylist_Data_Index(list, 1); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data2) == 0); + + data = Keylist_Data_Index(list, 2); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + + // work the data + data = Keylist_Data_Delete_By_Index(list, 1); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data2) == 0); + + ct_test(pTest, Keylist_Count(list) == 2); + + data = Keylist_Data_Index(list, 0); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data3) == 0); + + data = Keylist_Data_Index(list, 1); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + + data = Keylist_Data_Delete_By_Index(list, 1); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + + data = Keylist_Data_Delete_By_Index(list, 1); + ct_test(pTest, data == NULL); + + // cleanup + do { + data = Keylist_Data_Pop(list); + } + while (data); + + Keylist_Delete(list); + + return; +} + +// test access of a lot of entries +void testKeyListLarge(Test * pTest) +{ + int data1 = 42; + int *data; + OS_Keylist list; + KEY key; + int index; + const unsigned num_keys = 1024 * 16; + + list = Keylist_Create(); + if (!list) + return; + + for (key = 0; key < num_keys; key++) { + index = Keylist_Data_Add(list, key, &data1); + + } + for (key = 0; key < num_keys; key++) { + data = Keylist_Data(list, key); + ct_test(pTest, *data == data1); + } + for (index = 0; index < num_keys; index++) { + data = Keylist_Data_Index(list, index); + ct_test(pTest, *data == data1); + } + Keylist_Delete(list); + + return; +} + +#ifdef TEST_KEYLIST +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("keylist", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testKeyListFIFO); + assert(rc); + rc = ct_addTestFunction(pTest, testKeyListFILO); + assert(rc); + rc = ct_addTestFunction(pTest, testKeyListDataKey); + assert(rc); + rc = ct_addTestFunction(pTest, testKeySample); + assert(rc); + rc = ct_addTestFunction(pTest, testKeyListDataIndex); + assert(rc); + rc = ct_addTestFunction(pTest, testKeyListLarge); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_KEYLIST */ +#endif /* TEST */ diff --git a/bacnet-stack/keylist.h b/bacnet-stack/keylist.h new file mode 100644 index 00000000..9faa99d1 --- /dev/null +++ b/bacnet-stack/keylist.h @@ -0,0 +1,93 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 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####*/ +#ifndef KEYLIST_H +#define KEYLIST_H + +#include "key.h" + +// This is a key sorted linked list data library that +// uses a key or index to access the data. +// If the keys are duplicated, they can be added into the list like FIFO + +// list data and datatype +struct Keylist_Node { + KEY key; // unique number that is sorted in the list + void *data; // pointer to some data that is stored +}; + +typedef struct Keylist { + struct Keylist_Node **array; // array of nodes + int count; // number of nodes in this list - more effecient than loop + int size; // number of available nodes on this list - can grow or shrink +} KEYLIST_TYPE; +typedef KEYLIST_TYPE *OS_Keylist; + +// returns head of the list or NULL on failure. +OS_Keylist Keylist_Create(void); + +// delete specified list +// note: you should pop all the nodes off the list first. +void Keylist_Delete(OS_Keylist list); + +// inserts a node into its sorted position +// returns the index where it was added +int Keylist_Data_Add(OS_Keylist list, KEY key, void *data); + +// deletes a node specified by its key +// returns the data from the node +void *Keylist_Data_Delete(OS_Keylist list, KEY key); + +// deletes a node specified by its index +// returns the data from the node +void *Keylist_Data_Delete_By_Index(OS_Keylist list, int index); + +// returns the data from last node, and removes it from the list +void *Keylist_Data_Pop(OS_Keylist list); + +// returns the data from the node specified by key +void *Keylist_Data(OS_Keylist list, KEY key); + +// returns the data specified by key +void *Keylist_Data_Index(OS_Keylist list, int index); + +// return the key at the given index +KEY Keylist_Key(OS_Keylist list, int index); + +// returns the next empty key from the list +KEY Keylist_Next_Empty_Key(OS_Keylist list, KEY key); + +// returns the number of items in the list +int Keylist_Count(OS_Keylist list); + +#endif diff --git a/bacnet-stack/keylist.mak b/bacnet-stack/keylist.mak new file mode 100644 index 00000000..e6c1abd2 --- /dev/null +++ b/bacnet-stack/keylist.mak @@ -0,0 +1,38 @@ +#Makefile to build unit tests +CC = gcc +BASEDIR = . +#CFLAGS = -Wall -I. +# -g for debugging with gdb +CFLAGS = -Wall -I. -Itest -g -DTEST -DTEST_KEY -DTEST_KEYLIST + +KEY_SRCS = key.c \ + test/ctest.c + +KEYLIST_SRCS = keylist.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +KEY_OBJS = ${KEY_SRCS:.c=.o} + +KEYLIST_OBJS = ${KEYLIST_SRCS:.c=.o} + +all: key keylist + +key: ${KEY_OBJS} + ${CC} -o $@ ${KEY_OBJS} + +keylist: ${KEYLIST_OBJS} + ${CC} -o $@ ${KEYLIST_OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 + +include: .depend