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 7c541de7..1b429d97 100644 Binary files a/bacnet-stack/ports/win32/bacnet.ide and b/bacnet-stack/ports/win32/bacnet.ide differ 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;