From 5fcf45a781d9f02a8ae98806b182bc823db2a12a Mon Sep 17 00:00:00 2001 From: skarg Date: Thu, 16 Feb 2006 21:35:06 +0000 Subject: [PATCH] Created I-Have service encoding/decoding/unit test. Added handlers for I-Have and Who-Has. Added send for I-Have and Who-Has. Created demo for WhoHas. Added I-Have handling in server demo. --- bacnet-stack/demo/handler/client.h | 22 ++- bacnet-stack/demo/handler/h_ihave.c | 62 +++++++ bacnet-stack/demo/handler/h_whohas.c | 99 ++++++++++ bacnet-stack/demo/handler/handlers.h | 12 +- bacnet-stack/demo/handler/s_ihave.c | 85 +++++++++ bacnet-stack/demo/handler/s_whohas.c | 125 +++++++++++++ bacnet-stack/demo/object/ai.c | 17 +- bacnet-stack/demo/object/ai.h | 1 + bacnet-stack/demo/object/ao.c | 18 +- bacnet-stack/demo/object/ao.h | 1 + bacnet-stack/demo/object/device.c | 142 +++++++++++---- bacnet-stack/demo/object/device.h | 8 + bacnet-stack/demo/server/Makefile | 4 + bacnet-stack/demo/server/makefile.b32 | 13 +- bacnet-stack/demo/server/server.c | 3 + bacnet-stack/demo/whohas/Makefile | 79 ++++++++ bacnet-stack/demo/whohas/main.c | 252 ++++++++++++++++++++++++++ bacnet-stack/demo/whohas/makefile.b32 | 148 +++++++++++++++ bacnet-stack/ihave.c | 228 +++++++++++++++++++++++ bacnet-stack/ihave.h | 76 ++++++++ bacnet-stack/ihave.mak | 34 ++++ bacnet-stack/ports/win32/bacnet.ide | Bin 64666 -> 64538 bytes bacnet-stack/whohas.c | 2 +- bacnet-stack/whohas.h | 6 +- 24 files changed, 1383 insertions(+), 54 deletions(-) create mode 100644 bacnet-stack/demo/handler/h_ihave.c create mode 100644 bacnet-stack/demo/handler/h_whohas.c create mode 100644 bacnet-stack/demo/handler/s_ihave.c create mode 100644 bacnet-stack/demo/handler/s_whohas.c create mode 100644 bacnet-stack/demo/whohas/Makefile create mode 100644 bacnet-stack/demo/whohas/main.c create mode 100644 bacnet-stack/demo/whohas/makefile.b32 create mode 100644 bacnet-stack/ihave.c create mode 100644 bacnet-stack/ihave.h create mode 100644 bacnet-stack/ihave.mak diff --git a/bacnet-stack/demo/handler/client.h b/bacnet-stack/demo/handler/client.h index 60713e95..ea639134 100644 --- a/bacnet-stack/demo/handler/client.h +++ b/bacnet-stack/demo/handler/client.h @@ -1,6 +1,6 @@ /************************************************************************** * -* Copyright (C) 2005 Steve Karg +* Copyright (C) 2006 Steve Karg * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -37,10 +37,28 @@ extern "C" { #endif /* __cplusplus */ +/* unconfirmed requests */ void Send_WhoIs( int32_t low_limit, int32_t high_limit); +void Send_WhoHas_Object( + int32_t low_limit, + int32_t high_limit, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance); + +void Send_WhoHas_Name( + int32_t low_limit, + int32_t high_limit, + char *object_name); + +void Send_I_Have( + uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + char *object_name); + /* returns the invoke ID for confirmed request, or 0 if failed */ uint8_t Send_Read_Property_Request( uint32_t device_id, /* destination device */ @@ -71,7 +89,7 @@ uint8_t Send_Device_Communication_Control_Request( uint16_t timeDuration, /* 0=optional */ BACNET_COMMUNICATION_ENABLE_DISABLE state, char *password); /* NULL=optional */ - + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/bacnet-stack/demo/handler/h_ihave.c b/bacnet-stack/demo/handler/h_ihave.c new file mode 100644 index 00000000..f5dec8ed --- /dev/null +++ b/bacnet-stack/demo/handler/h_ihave.c @@ -0,0 +1,62 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bactext.h" +#include "ihave.h" + +void handler_i_have( + uint8_t *service_request, + uint16_t service_len, + BACNET_ADDRESS *src) +{ + int len = 0; + BACNET_I_HAVE_DATA data; + + (void)service_len; + (void)src; + len = ihave_decode_service_request( + service_request, + service_len, + &data); + if (len != -1) + { + fprintf(stderr,"I-Have: %s %d from %s %u!\r\n", + bactext_object_type_name(data.object_id.type), + data.object_id.instance, + bactext_object_type_name(data.device_id.type), + data.device_id.instance); + } + else + fprintf(stderr,"I-Have: received, but unable to decode!\n"); + + return; +} + diff --git a/bacnet-stack/demo/handler/h_whohas.c b/bacnet-stack/demo/handler/h_whohas.c new file mode 100644 index 00000000..44eb526a --- /dev/null +++ b/bacnet-stack/demo/handler/h_whohas.c @@ -0,0 +1,99 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "whohas.h" +#include "device.h" +#include "client.h" + +void handler_who_has( + uint8_t *service_request, + uint16_t service_len, + BACNET_ADDRESS *src) +{ + int len = 0; + BACNET_WHO_HAS_DATA data; + bool directed_to_me = false; + int object_type = 0; + uint32_t object_instance = 0; + char *object_name = NULL; + bool found = false; + + (void)src; + len = whohas_decode_service_request( + service_request, + service_len, + &data); + if (len > 0) + { + if ((data.low_limit == -1) || (data.high_limit == -1)) + directed_to_me = true; + else if ((Device_Object_Instance_Number() >= (uint32_t)data.low_limit) && + (Device_Object_Instance_Number() <= (uint32_t)data.high_limit)) + directed_to_me = true; + if (directed_to_me) + { + /* do we have such an object? If so, send an I-Have. + note: we should have only 1 of such an object */ + if (data.object_name) + { + /* valid name in my device? */ + object_name = characterstring_value(&data.object.name); + found = Device_Valid_Object_Name( + object_name, + &object_type, + &object_instance); + if (found) + Send_I_Have( + Device_Object_Instance_Number(), + object_type, + object_instance, + object_name); + } + else + { + /* valid object in my device? */ + object_name = Device_Valid_Object_Id( + data.object.identifier.type, + data.object.identifier.instance); + if (object_name) + Send_I_Have( + Device_Object_Instance_Number(), + data.object.identifier.type, + data.object.identifier.instance, + object_name); + } + } + } + + return; +} diff --git a/bacnet-stack/demo/handler/handlers.h b/bacnet-stack/demo/handler/handlers.h index 65d4a220..be920eab 100644 --- a/bacnet-stack/demo/handler/handlers.h +++ b/bacnet-stack/demo/handler/handlers.h @@ -1,6 +1,6 @@ /************************************************************************** * -* Copyright (C) 2005 Steve Karg +* Copyright (C) 2005-2006 Steve Karg * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -49,6 +49,11 @@ void handler_who_is( uint8_t *service_request, uint16_t service_len, BACNET_ADDRESS *src); + +void handler_who_has( + uint8_t *service_request, + uint16_t service_len, + BACNET_ADDRESS *src); void handler_i_am_add( uint8_t *service_request, @@ -102,6 +107,11 @@ void handler_device_communication_control( BACNET_ADDRESS *src, BACNET_CONFIRMED_SERVICE_DATA *service_data); +void handler_i_have( + uint8_t *service_request, + uint16_t service_len, + BACNET_ADDRESS *src); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/bacnet-stack/demo/handler/s_ihave.c b/bacnet-stack/demo/handler/s_ihave.c new file mode 100644 index 00000000..5b46f933 --- /dev/null +++ b/bacnet-stack/demo/handler/s_ihave.c @@ -0,0 +1,85 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#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 "ihave.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" + +/* find a specific device, or use -1 for limit if you want unlimited */ +void Send_I_Have( + uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + char *object_name) +{ + int pdu_len = 0; + BACNET_ADDRESS dest; + int bytes_sent = 0; + BACNET_I_HAVE_DATA data; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return; + /* Who-Has is a global broadcast */ + datalink_get_broadcast_address(&dest); + /* encode the NPDU portion of the packet */ + pdu_len = npdu_encode_apdu( + &Handler_Transmit_Buffer[0], + &dest, + NULL, + false, /* true for confirmed messages */ + MESSAGE_PRIORITY_NORMAL); + /* encode the APDU portion of the packet */ + data.device_id.type = OBJECT_DEVICE; + data.device_id.instance = device_id; + data.object_id.type = object_type; + data.object_id.instance = object_instance; + characterstring_init_ansi(&data.object_name,object_name); + pdu_len += ihave_encode_apdu( + &Handler_Transmit_Buffer[pdu_len], + &data); + /* send the data */ + bytes_sent = datalink_send_pdu( + &dest, // destination address + &Handler_Transmit_Buffer[0], + pdu_len); // number of bytes of data + if (bytes_sent <= 0) + fprintf(stderr,"Failed to Send I-Have Reply (%s)!\n", strerror(errno)); +} diff --git a/bacnet-stack/demo/handler/s_whohas.c b/bacnet-stack/demo/handler/s_whohas.c new file mode 100644 index 00000000..916269b4 --- /dev/null +++ b/bacnet-stack/demo/handler/s_whohas.c @@ -0,0 +1,125 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#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 "whohas.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" + +/* find a specific device, or use -1 for limit if you want unlimited */ +void Send_WhoHas_Name( + int32_t low_limit, + int32_t high_limit, + char *object_name) +{ + int pdu_len = 0; + BACNET_ADDRESS dest; + int bytes_sent = 0; + BACNET_WHO_HAS_DATA data; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return; + /* Who-Has is a global broadcast */ + datalink_get_broadcast_address(&dest); + /* encode the NPDU portion of the packet */ + pdu_len = npdu_encode_apdu( + &Handler_Transmit_Buffer[0], + &dest, + NULL, + false, /* true for confirmed messages */ + MESSAGE_PRIORITY_NORMAL); + /* encode the APDU portion of the packet */ + data.low_limit = low_limit; + data.high_limit = high_limit; + data.object_name = true; + characterstring_init_ansi(&data.object.name,object_name); + pdu_len += whohas_encode_apdu( + &Handler_Transmit_Buffer[pdu_len], + &data); + /* send the data */ + bytes_sent = datalink_send_pdu( + &dest, // destination address + &Handler_Transmit_Buffer[0], + pdu_len); // number of bytes of data + if (bytes_sent <= 0) + fprintf(stderr,"Failed to Send Who-Has Request (%s)!\n", strerror(errno)); +} + +/* find a specific device, or use -1 for limit if you want unlimited */ +void Send_WhoHas_Object( + int32_t low_limit, + int32_t high_limit, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance) +{ + int pdu_len = 0; + BACNET_ADDRESS dest; + int bytes_sent = 0; + BACNET_WHO_HAS_DATA data; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return; + /* Who-Has is a global broadcast */ + datalink_get_broadcast_address(&dest); + /* encode the NPDU portion of the packet */ + pdu_len = npdu_encode_apdu( + &Handler_Transmit_Buffer[0], + &dest, + NULL, + false, /* true for confirmed messages */ + MESSAGE_PRIORITY_NORMAL); + /* encode the APDU portion of the packet */ + data.low_limit = low_limit; + data.high_limit = high_limit; + data.object_name = false; + data.object.identifier.type = object_type; + data.object.identifier.instance = object_instance; + pdu_len += whohas_encode_apdu( + &Handler_Transmit_Buffer[pdu_len], + &data); + /* send the data */ + bytes_sent = datalink_send_pdu( + &dest, // destination address + &Handler_Transmit_Buffer[0], + pdu_len); // number of bytes of data + if (bytes_sent <= 0) + fprintf(stderr,"Failed to Send Who-Has Request (%s)!\n", strerror(errno)); +} diff --git a/bacnet-stack/demo/object/ai.c b/bacnet-stack/demo/object/ai.c index 38c6f1a7..adbe16b6 100644 --- a/bacnet-stack/demo/object/ai.c +++ b/bacnet-stack/demo/object/ai.c @@ -61,7 +61,21 @@ uint32_t Analog_Input_Index_To_Instance(unsigned index) return index; } +char *Analog_Input_Name(uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + + if (object_instance < MAX_ANALOG_INPUTS) + { + sprintf(text_string,"ANALOG INPUT %u",object_instance); + return text_string; + } + + return NULL; +} + /* return apdu length, or -1 on error */ +/* assumption - object has already exists */ int Analog_Input_Encode_Property_APDU( uint8_t *apdu, uint32_t object_instance, @@ -85,8 +99,7 @@ int Analog_Input_Encode_Property_APDU( break; case PROP_OBJECT_NAME: case PROP_DESCRIPTION: - sprintf(text_string,"ANALOG INPUT %u",object_instance); - characterstring_init_ansi(&char_string, text_string); + characterstring_init_ansi(&char_string, Analog_Input_Name(object_instance)); apdu_len = encode_tagged_character_string(&apdu[0], &char_string); break; diff --git a/bacnet-stack/demo/object/ai.h b/bacnet-stack/demo/object/ai.h index a004932b..09e3a447 100644 --- a/bacnet-stack/demo/object/ai.h +++ b/bacnet-stack/demo/object/ai.h @@ -36,6 +36,7 @@ extern "C" { bool Analog_Input_Valid_Instance(uint32_t object_instance); unsigned Analog_Input_Count(void); uint32_t Analog_Input_Index_To_Instance(unsigned index); +char *Analog_Input_Name(uint32_t object_instance); int Analog_Input_Encode_Property_APDU( uint8_t *apdu, diff --git a/bacnet-stack/demo/object/ao.c b/bacnet-stack/demo/object/ao.c index 85126f28..61007f6b 100644 --- a/bacnet-stack/demo/object/ao.c +++ b/bacnet-stack/demo/object/ao.c @@ -141,6 +141,20 @@ static float Analog_Output_Present_Value(uint32_t object_instance) return value; } +/* note: the object name must be unique within this device */ +char *Analog_Output_Name(uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + + if (object_instance < MAX_ANALOG_OUTPUTS) + { + sprintf(text_string,"ANALOG OUTPUT %u",object_instance); + return text_string; + } + + return NULL; +} + /* return apdu len, or -1 on error */ int Analog_Output_Encode_Property_APDU( uint8_t *apdu, @@ -168,9 +182,7 @@ int Analog_Output_Encode_Property_APDU( break; case PROP_OBJECT_NAME: case PROP_DESCRIPTION: - // note: the object name must be unique within this device - sprintf(text_string,"ANALOG OUTPUT %u",object_instance); - characterstring_init_ansi(&char_string, text_string); + characterstring_init_ansi(&char_string, Analog_Output_Name(object_instance)); apdu_len = encode_tagged_character_string(&apdu[0], &char_string); break; diff --git a/bacnet-stack/demo/object/ao.h b/bacnet-stack/demo/object/ao.h index db8de2ce..bd8bfea0 100644 --- a/bacnet-stack/demo/object/ao.h +++ b/bacnet-stack/demo/object/ao.h @@ -38,6 +38,7 @@ extern "C" { bool Analog_Output_Valid_Instance(uint32_t object_instance); unsigned Analog_Output_Count(void); uint32_t Analog_Output_Index_To_Instance(unsigned index); +char *Analog_Output_Name(uint32_t object_instance); int Analog_Output_Encode_Property_APDU( uint8_t *apdu, diff --git a/bacnet-stack/demo/object/device.c b/bacnet-stack/demo/object/device.c index fc2d1bf2..4a783112 100644 --- a/bacnet-stack/demo/object/device.c +++ b/bacnet-stack/demo/object/device.c @@ -29,60 +29,65 @@ #include "bacdef.h" #include "bacdcode.h" #include "bacenum.h" -#include "config.h" // the custom stuff +#include "config.h" /* the custom stuff */ #include "apdu.h" -#include "ai.h" // object list dependency -#include "ao.h" // object list dependency -#include "wp.h" // write property handling +#include "ai.h" /* object list dependency */ +#include "ao.h" /* object list dependency */ +#include "wp.h" /* write property handling */ +#include "device.h" /* me */ #if BACFILE -#include "bacfile.h" // object list dependency +#include "bacfile.h" /* object list dependency */ #endif +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ static uint32_t Object_Instance_Number = 0; static char Object_Name[16] = "SimpleServer"; static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; static char Vendor_Name[16] = "ASHRAE"; -// vendor id assigned by ASHRAE +/* FIXME: your vendor id assigned by ASHRAE */ static uint16_t Vendor_Identifier = 0; static char Model_Name[16] = "GNU"; static char Firmware_Revision[16] = "1.0"; static char Application_Software_Version[16] = "1.0"; static char Location[16] = "USA"; static char Description[16] = "server"; -//static uint8_t Protocol_Version = 1; - constant, not settable -//static uint8_t Protocol_Revision = 4; - constant, not settable -//Protocol_Services_Supported -//Protocol_Object_Types_Supported -//Object_List -//static uint16_t Max_APDU_Length_Accepted = MAX_APDU; - constant -//static BACNET_SEGMENTATION Segmentation_Supported = SEGMENTATION_NONE; -//static uint8_t Max_Segments_Accepted = 0; -//VT_Classes_Supported -//Active_VT_Sessions -//Local_Time - rely on OS, if there is one -//Local_Date - rely on OS, if there is one -//UTC_Offset - rely on OS, if there is one -//Daylight_Savings_Status - rely on OS, if there is one -//APDU_Segment_Timeout +/* static uint8_t Protocol_Version = 1; - constant, not settable */ +/* static uint8_t Protocol_Revision = 4; - constant, not settable */ +/* Protocol_Services_Supported - dynamically generated */ +/* Protocol_Object_Types_Supported - in RP encoding */ +/* Object_List - dynamically generated */ +/* static uint16_t Max_APDU_Length_Accepted = MAX_APDU; - constant */ +/* static BACNET_SEGMENTATION Segmentation_Supported = SEGMENTATION_NONE; */ +/* static uint8_t Max_Segments_Accepted = 0; */ +/* VT_Classes_Supported */ +/* Active_VT_Sessions */ +/* Local_Time - rely on OS, if there is one */ +/* Local_Date - rely on OS, if there is one */ +/* UTC_Offset - rely on OS, if there is one */ +/* Daylight_Savings_Status - rely on OS, if there is one */ +/* APDU_Segment_Timeout */ static uint16_t APDU_Timeout = 3000; static uint8_t Number_Of_APDU_Retries = 3; -//List_Of_Session_Keys -//Time_Synchronization_Recipients -//Max_Master - rely on MS/TP subsystem, if there is one -//Max_Info_Frames - rely on MS/TP subsystem, if there is one -//Device_Address_Binding - required, but relies on binding cache +/* List_Of_Session_Keys */ +/* Time_Synchronization_Recipients */ +/* Max_Master - rely on MS/TP subsystem, if there is one */ +/* Max_Info_Frames - rely on MS/TP subsystem, if there is one */ +/* Device_Address_Binding - required, but relies on binding cache */ static uint8_t Database_Revision = 0; -//Configuration_Files -//Last_Restore_Time -//Backup_Failure_Timeout -//Active_COV_Subscriptions -//Slave_Proxy_Enable -//Manual_Slave_Address_Binding -//Auto_Slave_Discovery -//Slave_Address_Binding -//Profile_Name +/* Configuration_Files */ +/* Last_Restore_Time */ +/* Backup_Failure_Timeout */ +/* Active_COV_Subscriptions */ +/* Slave_Proxy_Enable */ +/* Manual_Slave_Address_Binding */ +/* Auto_Slave_Discovery */ +/* Slave_Address_Binding */ +/* Profile_Name */ -// methods to manipulate the data +/* methods to manipulate the data */ uint32_t Device_Object_Instance_Number(void) { return Object_Instance_Number; @@ -379,6 +384,71 @@ bool Device_Object_List_Identifier(unsigned array_index, return status; } +bool Device_Valid_Object_Name( + const char *object_name, + int *object_type, + uint32_t *object_instance) +{ + bool found = false; + int type = 0; + uint32_t instance; + unsigned max_objects = 0, i = 0; + bool check_id = false; + char *name = NULL; + + max_objects = Device_Object_List_Count(); + for (i = 0; i < max_objects; i++) + { + check_id = Device_Object_List_Identifier(i, &type, &instance); + if (check_id) + { + name = Device_Valid_Object_Id(type,instance); + if (strcmp(name,object_name) == 0) + { + found = true; + if (object_type) + *object_type = type; + if (object_instance) + *object_instance = instance; + break; + } + } + } + + return found; +} + +/* returns the name or NULL if not found */ +char *Device_Valid_Object_Id( + int object_type, + uint32_t object_instance) +{ + char *name = NULL; /* return value */ + + switch (object_type) + { + case OBJECT_ANALOG_INPUT: + name = Analog_Input_Name(object_instance); + break; + case OBJECT_ANALOG_OUTPUT: + name = Analog_Output_Name(object_instance); + break; +#if BACFILE + case OBJECT_FILE: + name = bacfile_name(object_instance); + break; +#endif + case OBJECT_DEVICE: + if (object_instance == Object_Instance_Number) + name = Object_Name; + break; + default: + break; + } + + return name; +} + // return the length of the apdu encoded or -1 for error int Device_Encode_Property_APDU( uint8_t *apdu, diff --git a/bacnet-stack/demo/object/device.h b/bacnet-stack/demo/object/device.h index 66919708..438fe4f4 100644 --- a/bacnet-stack/demo/object/device.h +++ b/bacnet-stack/demo/object/device.h @@ -91,6 +91,14 @@ void Device_Set_Number_Of_APDU_Retries(uint8_t retries); uint8_t Device_Database_Revision(void); void Device_Set_Database_Revision(uint8_t revision); +bool Device_Valid_Object_Name( + const char *object_name, + int *object_type, + uint32_t *object_instance); +char *Device_Valid_Object_Id( + int object_type, + uint32_t object_instance); + int Device_Encode_Property_APDU( uint8_t *apdu, BACNET_PROPERTY_ID property, diff --git a/bacnet-stack/demo/server/Makefile b/bacnet-stack/demo/server/Makefile index 6cded1c7..6b43ab50 100644 --- a/bacnet-stack/demo/server/Makefile +++ b/bacnet-stack/demo/server/Makefile @@ -31,6 +31,8 @@ SRCS = server.c \ $(BACNET_HANDLER)/h_arf.c \ $(BACNET_HANDLER)/h_rd.c \ $(BACNET_HANDLER)/h_dcc.c \ + $(BACNET_HANDLER)/h_whohas.c \ + $(BACNET_HANDLER)/s_ihave.c \ $(BACNET_OBJECT)/device.c \ $(BACNET_OBJECT)/ai.c \ $(BACNET_OBJECT)/ao.c \ @@ -46,6 +48,8 @@ SRCS = server.c \ $(BACNET_ROOT)/bigend.c \ $(BACNET_ROOT)/whois.c \ $(BACNET_ROOT)/iam.c \ + $(BACNET_ROOT)/whohas.c \ + $(BACNET_ROOT)/ihave.c \ $(BACNET_ROOT)/rp.c \ $(BACNET_ROOT)/wp.c \ $(BACNET_ROOT)/arf.c \ diff --git a/bacnet-stack/demo/server/makefile.b32 b/bacnet-stack/demo/server/makefile.b32 index 268789e0..bddf6b22 100644 --- a/bacnet-stack/demo/server/makefile.b32 +++ b/bacnet-stack/demo/server/makefile.b32 @@ -30,6 +30,8 @@ SRCS = server.c \ ..\..\demo\handler\h_arf.c \ ..\..\demo\handler\h_rd.c \ ..\..\demo\handler\h_dcc.c \ + ..\..\demo\handler\h_whohas.c \ + ..\..\demo\handler\s_ihave.c \ ..\..\bacdcode.c \ ..\..\bacapp.c \ ..\..\bacstr.c \ @@ -38,6 +40,8 @@ SRCS = server.c \ ..\..\bigend.c \ ..\..\whois.c \ ..\..\iam.c \ + ..\..\whohas.c \ + ..\..\ihave.c \ ..\..\rp.c \ ..\..\wp.c \ ..\..\arf.c \ @@ -59,7 +63,8 @@ OBJS = $(SRCS:.c=.obj) # Compiler definitions # -CC = $(BORLAND_DIR)\bin\bcc32 +bcc32.cfg +BCC_CFG = bcc32.cfg +CC = $(BORLAND_DIR)\bin\bcc32 +$(BCC_CFG) #LINK = $(BORLAND_DIR)\bin\tlink32 LINK = $(BORLAND_DIR)\bin\ilink32 TLIB = $(BORLAND_DIR)\bin\tlib @@ -84,7 +89,7 @@ $(C_LIB_DIR)\CW32MT.lib # # This should be the first one in the makefile -all : bcc32.cfg $(PRODUCT_EXE) +all : $(BCC_CFG) $(PRODUCT_EXE) # Linker specific: the link below is for BCC linker/compiler. If you link # with a different linker - please change accordingly. @@ -113,7 +118,7 @@ clean : del ..\..\ports\win32\*.obj del $(PRODUCT_EXE) del *.map - del bcc32.cfg + del $(BCC_CFG) # # Generic rules @@ -127,7 +132,7 @@ clean : $(CC) -o$@ $< # Compiler configuration file -bcc32.cfg : +$(BCC_CFG) : Copy &&| $(CFLAGS) -c diff --git a/bacnet-stack/demo/server/server.c b/bacnet-stack/demo/server/server.c index 13ebfe7c..68030a22 100644 --- a/bacnet-stack/demo/server/server.c +++ b/bacnet-stack/demo/server/server.c @@ -56,6 +56,9 @@ static void Init_Service_Handlers(void) apdu_set_unconfirmed_handler( SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler( + SERVICE_UNCONFIRMED_WHO_HAS, + handler_who_has); /* 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( diff --git a/bacnet-stack/demo/whohas/Makefile b/bacnet-stack/demo/whohas/Makefile new file mode 100644 index 00000000..35ad42cf --- /dev/null +++ b/bacnet-stack/demo/whohas/Makefile @@ -0,0 +1,79 @@ +#Makefile to build BACnet Application for the Linux Port +CC = gcc +BASEDIR = . +#CFLAGS = -Wall -I. +# -g for debugging with gdb +#CFLAGS = -Wall -I. -O2 -g +# Note: you can strip out symbols using the strip command +# to get an idea of how big the compile really is. +#DEFINES = -DBACFILE=1 -DBACDL_ETHERNET=1 +#DEFINES = -DBACFILE=1 -DBACDL_ARCNET=1 +#DEFINES = -DBACFILE=1 -DBACDL_MSTP=1 +DEFINES = -DBACFILE=1 -DTSM_ENABLED=1 -DBACDL_BIP=1 +BACNET_PORT = ../../ports/linux +BACNET_OBJECT = ../object +BACNET_HANDLER = ../handler +BACNET_ROOT = ../.. +INCLUDES = -I$(BACNET_ROOT) -I$(BACNET_PORT) -I$(BACNET_OBJECT) -I$(BACNET_HANDLER) + +CFLAGS = -Wall -g $(INCLUDES) $(DEFINES) + +TARGET = bacrd + +SRCS = main.c \ + $(BACNET_ROOT)/rd.c \ + $(BACNET_PORT)/bip-init.c \ + $(BACNET_ROOT)/bip.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)/s_whois.c \ + $(BACNET_HANDLER)/s_rd.c \ + $(BACNET_OBJECT)/device.c \ + $(BACNET_OBJECT)/ai.c \ + $(BACNET_OBJECT)/ao.c \ + $(BACNET_OBJECT)/bacfile.c \ + $(BACNET_ROOT)/filename.c \ + $(BACNET_ROOT)/rp.c \ + $(BACNET_ROOT)/wp.c \ + $(BACNET_ROOT)/bacdcode.c \ + $(BACNET_ROOT)/bacapp.c \ + $(BACNET_ROOT)/bacprop.c \ + $(BACNET_ROOT)/bacstr.c \ + $(BACNET_ROOT)/bactext.c \ + $(BACNET_ROOT)/indtext.c \ + $(BACNET_ROOT)/bigend.c \ + $(BACNET_ROOT)/whois.c \ + $(BACNET_ROOT)/iam.c \ + $(BACNET_ROOT)/tsm.c \ + $(BACNET_ROOT)/datalink.c \ + $(BACNET_ROOT)/address.c \ + $(BACNET_ROOT)/arf.c \ + $(BACNET_ROOT)/dcc.c \ + $(BACNET_ROOT)/abort.c \ + $(BACNET_ROOT)/reject.c \ + $(BACNET_ROOT)/bacerror.c \ + $(BACNET_ROOT)/apdu.c \ + $(BACNET_ROOT)/npdu.c + +OBJS = ${SRCS:.c=.o} + +all: ${TARGET} + +${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 ports/linux/*.bak *.1 *.ini + +include: .depend + diff --git a/bacnet-stack/demo/whohas/main.c b/bacnet-stack/demo/whohas/main.c new file mode 100644 index 00000000..1ed195b9 --- /dev/null +++ b/bacnet-stack/demo/whohas/main.c @@ -0,0 +1,252 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* WRITEPROP: command line tool that writes a property to a BACnet device. */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whohas.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" + +// buffer used for receive +static uint8_t Rx_Buf[MAX_MPDU] = {0}; + +/* global variables used in this file */ +static BACNET_OBJECT_TYPE Target_Object_Type = MAX_BACNET_OBJECT_TYPE; +static uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE; +static char *Target_Object_Name = NULL; + +static bool Error_Detected = false; + +void MyAbortHandler( + BACNET_ADDRESS *src, + uint8_t invoke_id, + uint8_t abort_reason) +{ + /* FIXME: verify src and invoke id */ + (void)src; + (void)invoke_id; + printf("BACnet Abort: %s\r\n", + bactext_abort_reason_name(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(reject_reason)); + Error_Detected = true; +} + +static void Init_Service_Handlers(void) +{ + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler( + SERVICE_UNCONFIRMED_WHO_IS, + handler_who_is); + /* 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); + /* handle the reply (request) coming back */ + apdu_set_unconfirmed_handler( + SERVICE_UNCONFIRMED_I_HAVE, + handler_i_have); + /* handle any errors coming back */ + apdu_set_abort_handler( + MyAbortHandler); + apdu_set_reject_handler( + MyRejectHandler); +} + +#ifdef BIP_DEBUG +static void print_address( + char *name, + BACNET_ADDRESS *dest) // destination address +{ + int i = 0; // counter + + if (dest) + { + printf("%s: ",name); + for (i = 0; i < dest->mac_len; i++) + { + printf("%02X",dest->mac[i]); + } + printf("\n"); + } +} +#endif + +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; + #ifdef BIP_DEBUG + BACNET_ADDRESS my_address, broadcast_address; + #endif + + if (argc < 2) + { + /* note: priority 16 and 0 should produce the same end results... */ + printf( + "Usage: %s \r\n" + "Send BACnet WhoHas request to devices, and wait for responses.\r\n" + "\r\n" + "Use either:\r\n" + "The object-type can be 0 to %d.\r\n" + "The object-instance can be 0 to %d.\r\n" + "or:\r\n" + "The object-name can be any string of characters.\r\n", + filename_remove_path(argv[0]), + MAX_BACNET_OBJECT_TYPE-1, + BACNET_MAX_INSTANCE-1); + return 0; + } + /* decode the command line parameters */ + if (argc < 3) + { + Target_Object_Name = argv[1]; + } + else + { + Target_Object_Type = strtol(argv[1],NULL,0); + Target_Object_Instance = strtol(argv[2],NULL,0); + } + if (Target_Object_Instance > BACNET_MAX_INSTANCE) + { + fprintf(stderr,"object-instance=%u - it must be less than %u\r\n", + Target_Object_Instance,BACNET_MAX_INSTANCE+1); + return 1; + } + if (Target_Object_Type > MAX_BACNET_OBJECT_TYPE) + { + fprintf(stderr,"object-type=%u - it must be less than %u\r\n", + Target_Object_Type,MAX_BACNET_OBJECT_TYPE+1); + return 1; + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + /* configure standard BACnet/IP port */ + bip_set_interface("eth0"); /* for linux */ + bip_set_port(0xBAC0); + if (!bip_init()) + return 1; + #ifdef BIP_DEBUG + datalink_get_broadcast_address(&broadcast_address); + print_address("Broadcast",&broadcast_address); + datalink_get_my_address(&my_address); + print_address("Address",&my_address); + #endif + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = Device_APDU_Timeout() / 1000; + /* don't send an I-Am unless asked */ + I_Am_Request = false; + /* send the request */ + if (argc < 3) + Send_WhoHas_Name(-1, -1, Target_Object_Name); + else + Send_WhoHas_Object(-1, -1, + Target_Object_Type, + Target_Object_Instance); + /* loop forever */ + for (;;) + { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = bip_receive( + &src, + &Rx_Buf[0], + MAX_MPDU, + timeout); + /* process */ + if (pdu_len) + { + npdu_handler( + &src, + &Rx_Buf[0], + pdu_len); + } + if (Error_Detected) + break; + if (I_Am_Request) + { + I_Am_Request = false; + iam_send(&Handler_Transmit_Buffer[0]); + } + else + { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) + break; + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/bacnet-stack/demo/whohas/makefile.b32 b/bacnet-stack/demo/whohas/makefile.b32 new file mode 100644 index 00000000..3ab6a0e2 --- /dev/null +++ b/bacnet-stack/demo/whohas/makefile.b32 @@ -0,0 +1,148 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacwh +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_BIP=1;TSM_ENABLED=0;USE_INADDR=1 + +SRCS = main.c \ + ..\..\rd.c \ + ..\..\ports\win32\bip-init.c \ + ..\..\filename.c \ + ..\..\bip.c \ + ..\..\demo\handler\txbuf.c \ + ..\..\demo\handler\noserv.c \ + ..\..\demo\handler\h_whois.c \ + ..\..\demo\handler\h_iam.c \ + ..\..\demo\handler\h_ihave.c \ + ..\..\demo\handler\h_rp.c \ + ..\..\demo\handler\s_ihave.c \ + ..\..\demo\handler\s_whohas.c \ + ..\..\bacdcode.c \ + ..\..\bacapp.c \ + ..\..\bacstr.c \ + ..\..\bactext.c \ + ..\..\indtext.c \ + ..\..\bigend.c \ + ..\..\whois.c \ + ..\..\ihave.c \ + ..\..\whohas.c \ + ..\..\iam.c \ + ..\..\rp.c \ + ..\..\wp.c \ + ..\..\arf.c \ + ..\..\awf.c \ + ..\..\dcc.c \ + ..\..\demo\object\bacfile.c \ + ..\..\demo\object\device.c \ + ..\..\demo\object\ai.c \ + ..\..\demo\object\ao.c \ + ..\..\datalink.c \ + ..\..\abort.c \ + ..\..\reject.c \ + ..\..\bacerror.c \ + ..\..\apdu.c \ + ..\..\npdu.c + +OBJS = $(SRCS:.c=.obj) + +# Compiler definitions +# +CC = $(BORLAND_DIR)\bin\bcc32 +bcc32.cfg +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 +TLIB = $(BORLAND_DIR)\bin\tlib + +# +# Include directories +# +CC_DIR = $(BORLAND_DIR)\BIN +INCL_DIRS = -I$(BORLAND_DIR)\include;..\..\;..\..\demo\object\;..\..\demo\handler\;..\..\ports\win32\;. + +CFLAGS = $(INCL_DIRS) $(CS_FLAGS) $(DEFINES) + +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(C_LIB_DIR)\IMPORT32.lib \ +$(C_LIB_DIR)\CW32MT.lib + +# +# Main target +# +# This should be the first one in the makefile + +all : bcc32.cfg $(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&) because command line is too long +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -m -c -s -v @&&| # temp response file, starts with | + $(BORLAND_DIR)\lib\c0x32.obj $** # $** lists each dependency + $< + $*.map + $(LIBS) +| # end of temp response file + +# +# Utilities + +clean : + @echo Deleting obj files, $(PRODUCT_EXE) and map files. +# del $(OBJS) # command too long, bummer! + del *.obj + del ..\..\*.obj + del ..\..\demo\handler\*.obj + del ..\..\demo\object\*.obj + del ..\..\ports\win32\*.obj + del $(PRODUCT_EXE) + del *.map + del bcc32.cfg + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) -o$@ $< + +# Compiler configuration file +bcc32.cfg : + Copy &&| +$(CFLAGS) +-c +-y #include line numbers in OBJ's +-v #include debug info +-w+ #turn on all warnings +-Od #disable all optimizations +#-a4 #32 bit data alignment +#-M # generate link map +#-ls # linker options +#-WM- #not multithread +-WM #multithread +-w-aus # ignore warning assigned a value that is never used +-w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/bacnet-stack/ihave.c b/bacnet-stack/ihave.c new file mode 100644 index 00000000..92e97aae --- /dev/null +++ b/bacnet-stack/ihave.c @@ -0,0 +1,228 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2006 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 +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "ihave.h" + +int ihave_encode_apdu( + uint8_t *apdu, + BACNET_I_HAVE_DATA *data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu && data) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = SERVICE_UNCONFIRMED_I_HAVE; + apdu_len = 2; + /* deviceIdentifier */ + len = encode_tagged_object_id( + &apdu[apdu_len], + data->device_id.type, + data->device_id.instance); + apdu_len += len; + /* objectIdentifier */ + len = encode_tagged_object_id( + &apdu[apdu_len], + data->object_id.type, + data->object_id.instance); + apdu_len += len; + /* objectName */ + len = encode_tagged_character_string( + &apdu[apdu_len], + &data->object_name); + apdu_len += len; + } + + return apdu_len; +} + +/* decode the service request only */ +int ihave_decode_service_request( + uint8_t *apdu, + unsigned apdu_len, + BACNET_I_HAVE_DATA *data) +{ + int len = 0; + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t decoded_value = 0; /* for decoding */ + int decoded_type = 0; /* for decoding */ + + if (apdu_len && data) + { + /* deviceIdentifier */ + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) + { + len += decode_object_id(&apdu[len], &decoded_type, + &data->device_id.instance); + data->device_id.type = decoded_type; + } + else + return -1; + /* objectIdentifier */ + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) + { + len += decode_object_id(&apdu[len], &decoded_type, + &data->object_id.instance); + data->object_id.type = decoded_type; + } + else + return -1; + /* objectName */ + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING) + { + len += decode_character_string(&apdu[len], len_value, + &data->object_name); + } + else + return -1; + } + else + return -1; + + return len; +} + +int ihave_decode_apdu( + uint8_t *apdu, + unsigned apdu_len, + BACNET_I_HAVE_DATA *data) +{ + int len = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) + return -1; + if (apdu[1] != SERVICE_UNCONFIRMED_I_HAVE) + return -1; + len = ihave_decode_service_request( + &apdu[2], + apdu_len - 2, + data); + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testIHaveData(Test * pTest, BACNET_I_HAVE_DATA *data) +{ + uint8_t apdu[480] = {0}; + int len = 0; + int apdu_len = 0; + BACNET_I_HAVE_DATA test_data; + + len = ihave_encode_apdu( + &apdu[0], + data); + ct_test(pTest, len != 0); + apdu_len = len; + + len = ihave_decode_apdu( + &apdu[0], + apdu_len, + &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.device_id.type == + data->device_id.type); + ct_test(pTest, test_data.device_id.instance == + data->device_id.instance); + ct_test(pTest, test_data.object_id.type == + data->object_id.type); + ct_test(pTest, test_data.object_id.instance == + data->object_id.instance); + ct_test(pTest, characterstring_same( + &test_data.object_name,&data->object_name)); + } +} + +void testIHave(Test * pTest) +{ + BACNET_I_HAVE_DATA data; + + characterstring_init_ansi( + &data.object_name,"patricia"); + data.device_id.type = OBJECT_DEVICE; + for ( + data.device_id.instance = 1; + data.device_id.instance <= BACNET_MAX_INSTANCE; + data.device_id.instance <<= 1) + { + for ( + data.object_id.type = OBJECT_ANALOG_INPUT; + data.object_id.type <= MAX_BACNET_OBJECT_TYPE; + data.object_id.type++) + { + for ( + data.object_id.instance = 1; + data.object_id.instance <= BACNET_MAX_INSTANCE; + data.object_id.instance <<= 1) + { + testIHaveData(pTest,&data); + } + } + } +} + +#ifdef TEST_I_HAVE +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet I-Have", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testIHave); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_WHOIS */ +#endif /* TEST */ diff --git a/bacnet-stack/ihave.h b/bacnet-stack/ihave.h new file mode 100644 index 00000000..532b6b4f --- /dev/null +++ b/bacnet-stack/ihave.h @@ -0,0 +1,76 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2006 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 IHAVE_H +#define IHAVE_H + +#include +#include +#include "bacstr.h" + +typedef struct BACnet_I_Have_Data +{ + BACNET_OBJECT_ID device_id; + BACNET_OBJECT_ID object_id; + BACNET_CHARACTER_STRING object_name; +} BACNET_I_HAVE_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int ihave_encode_apdu( + uint8_t *apdu, + BACNET_I_HAVE_DATA *data); + +int ihave_decode_service_request( + uint8_t *apdu, + unsigned apdu_len, + BACNET_I_HAVE_DATA *data); + +int ihave_decode_apdu( + uint8_t *apdu, + unsigned apdu_len, + BACNET_I_HAVE_DATA *data); + +#ifdef TEST +#include "ctest.h" +void testIHave(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + diff --git a/bacnet-stack/ihave.mak b/bacnet-stack/ihave.mak new file mode 100644 index 00000000..f3325ed5 --- /dev/null +++ b/bacnet-stack/ihave.mak @@ -0,0 +1,34 @@ +#Makefile to build test case +CC = gcc +BASEDIR = . +#CFLAGS = -Wall -I. +# -g for debugging with gdb +#CFLAGS = -Wall -I. -g +CFLAGS = -Wall -I. -Itest -DTEST -DTEST_I_HAVE -g + +SRCS = bacdcode.c \ + bacstr.c \ + bigend.c \ + ihave.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = ihave + +all: ${TARGET} + +${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 diff --git a/bacnet-stack/ports/win32/bacnet.ide b/bacnet-stack/ports/win32/bacnet.ide index 7c541de79c97a6ebb810ff05e78a97dcec287e73..1b429d978db04ba37e50de11e6609e9ea8ea23bd 100644 GIT binary patch literal 64538 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 delta 17430 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 XV!}(|M0ng61{s7)2_a9q0@?on`@(QV diff --git a/bacnet-stack/whohas.c b/bacnet-stack/whohas.c index d3c19184..e9bdf204 100644 --- a/bacnet-stack/whohas.c +++ b/bacnet-stack/whohas.c @@ -48,7 +48,7 @@ int whohas_encode_apdu( if (apdu && data) { apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; - apdu[1] = SERVICE_UNCONFIRMED_WHO_IS; // service choice + apdu[1] = SERVICE_UNCONFIRMED_WHO_HAS; // service choice apdu_len = 2; // optional limits - must be used as a pair if ((data->low_limit >= 0) && (data->low_limit <= BACNET_MAX_INSTANCE) && diff --git a/bacnet-stack/whohas.h b/bacnet-stack/whohas.h index 6645e591..4743d135 100644 --- a/bacnet-stack/whohas.h +++ b/bacnet-stack/whohas.h @@ -45,11 +45,7 @@ typedef struct BACnet_Who_Has_Data bool object_name; /* true if a string */ union { - struct - { - BACNET_OBJECT_TYPE type; - uint32_t instance; - } identifier; + BACNET_OBJECT_ID identifier; BACNET_CHARACTER_STRING name; } object; } BACNET_WHO_HAS_DATA;