diff --git a/bacnet-stack/bacenum.h b/bacnet-stack/bacenum.h index 71834a4e..5494bc9d 100644 --- a/bacnet-stack/bacenum.h +++ b/bacnet-stack/bacenum.h @@ -675,12 +675,13 @@ typedef enum SERVICE_CONFIRMED_VT_DATA = 23, // Security Services SERVICE_CONFIRMED_AUTHENTICATE = 24, - SERVICE_CONFIRMED_REQUEST_KEY = 25 + SERVICE_CONFIRMED_REQUEST_KEY = 25, // Services added after 1995 // readRange (26) see Object Access Services // lifeSafetyOperation (27) see Alarm and Event Services // subscribeCOVProperty (28) see Alarm and Event Services // getEventInformation (29) see Alarm and Event Services + MAX_BACNET_CONFIRMED_SERVICE = 30 } BACNET_CONFIRMED_SERVICE; typedef enum { @@ -693,12 +694,13 @@ typedef enum { SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION = 6, SERVICE_UNCONFIRMED_WHO_HAS = 7, SERVICE_UNCONFIRMED_WHO_IS = 8, - SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION = 9 + SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION = 9, // Other services to be added as they are defined. // All choice values in this production are reserved // for definition by ASHRAE. // Proprietary extensions are made by using the // UnconfirmedPrivateTransfer service. See Clause 23. + MAX_BACNET_UNCONFIRMED_SERVICE = 10 } BACNET_UNCONFIRMED_SERVICE; typedef enum { @@ -781,6 +783,9 @@ typedef enum { // Enumerated values 0-63 are reserved for definition by ASHRAE. // Enumerated values 64-65535 may be used by others subject to // the procedures and constraints described in Clause 23. + MAX_BACNET_ABORT_REASON = 5, + FIRST_PROPRIETARY_ABORT_REASON = 64, + LAST_PROPRIETARY_ABORT_REASON = 65535 } BACNET_ABORT_REASON; typedef enum { @@ -797,6 +802,9 @@ typedef enum { // Enumerated values 0-63 are reserved for definition by ASHRAE. // Enumerated values 64-65535 may be used by others subject to // the procedures and constraints described in Clause 23. + MAX_BACNET_REJECT_REASON = 10, + FIRST_PROPRIETARY_REJECT_REASON = 64, + LAST_PROPRIETARY_REJECT_REASON = 65535 } BACNET_BACNET_REJECT_REASON; typedef enum { @@ -810,6 +818,9 @@ typedef enum { // Enumerated values 0-63 are reserved for definition by ASHRAE. // Enumerated values 64-65535 may be used by others subject to // the procedures and constraints described in Clause 23. + MAX_BACNET_ERROR_CLASS = 7, + FIRST_PROPRIETARY_ERROR_CLASS = 64, + LAST_PROPRIETARY_ERROR_CLASS = 65535 } BACNET_ERROR_CLASS; typedef enum { @@ -868,6 +879,9 @@ typedef enum { // Enumerated values 256-65535 may be used by others subject to // the procedures and constraints described in Clause 23. // The last enumeration used in this version is 46. + MAX_BACNET_ERROR_CODE = 47, + FIRST_PROPRIETARY_ERROR_CODE = 256, + LAST_PROPRIETARY_ERROR_CODE = 65535 } BACNET_ERROR_CODE; #endif // end of BACENUM_H diff --git a/bacnet-stack/device.c b/bacnet-stack/device.c index 6b2a4123..2c3a036a 100644 --- a/bacnet-stack/device.c +++ b/bacnet-stack/device.c @@ -39,6 +39,11 @@ #include "bacenum.h" #include "config.h" // the custom stuff +// vendor id assigned by ASHRAE +static uint32_t Object_Identifier = 0; +// FIXME: it is likely that this name is configurable, +// so consider a fixed sized string +static const char *Object_Name = "SimpleServer"; static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; static const char *Vendor_Name = "ASHRAE"; static uint16_t Vendor_Identifier = 0; @@ -81,7 +86,17 @@ static uint8_t Database_Revision = 0; //Profile_Name // methods to manipulate the data -// FIXME: add APDU encode methods for each? +uint32_t Device_Object_Identifier(void) +{ + return Object_Identifier; +} + +void Device_Set_Object_Identifier(uint32_t object_id) +{ + // FIXME: bounds check? + Object_Identifier = object_id; +} + BACNET_DEVICE_STATUS Device_System_Status(void) { return System_Status; @@ -186,6 +201,7 @@ uint16_t Device_APDU_Timeout(void) return APDU_Timeout; } +// in milliseconds void Device_Set_APDU_Timeout(uint16_t timeout) { APDU_Timeout = timeout; @@ -211,3 +227,138 @@ void Device_Set_Database_Revision(uint8_t revision) Database_Revision = revision; } +int Device_Encode_Property_APDU( + uint8_t *apdu, + BACNET_PROPERTY_ID property, + int array_index) +{ + int apdu_len = 0; // return value + + switch (property) + { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE, + Object_Identifier); + break; + case PROP_OBJECT_NAME: + apdu_len = encode_tagged_character_string(&apdu[0], Object_Name); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_DESCRIPTION: + apdu_len = encode_tagged_character_string(&apdu[0], Description); + break; + case PROP_SYSTEM_STATUS: + apdu_len = encode_tagged_enumerated(&apdu[0], System_Status); + break; + case PROP_VENDOR_NAME: + apdu_len = encode_tagged_character_string(&apdu[0], Vendor_Name); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = encode_tagged_unsigned(&apdu[0], Vendor_Identifier); + break; + case PROP_MODEL_NAME: + apdu_len = encode_tagged_character_string(&apdu[0], Model_Name); + break; + case PROP_FIRMWARE_REVISION: + apdu_len = encode_tagged_character_string(&apdu[0], Program_Version); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + apdu_len = encode_tagged_character_string(&apdu[0], + Application_Software_Version); + break; + // if you support time + case PROP_LOCAL_TIME: + //t = time(NULL); + //my_tm = localtime(&t); + //apdu_len = + // encode_tagged_time(&apdu[0], my_tm->tm_hour, my_tm->tm_min, + // my_tm->tm_sec, 0); + break; + // if you support date + case PROP_LOCAL_DATE: + //t = time(NULL); + //my_tm = localtime(&t); + // year = years since 1900 + // month 1=Jan + // day = day of month + // wday 1=Monday...7=Sunday + //apdu_len = encode_tagged_date(&apdu[0], + // my_tm->tm_year, + // my_tm->tm_mon + 1, + // my_tm->tm_mday, ((my_tm->tm_wday == 0) ? 7 : my_tm->tm_wday)); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = encode_tagged_unsigned(&apdu[0], Protocol_Version); + break; + // Legacy Support - necessary? + //case PROP_PROTOCOL_CONFORMANCE_CLASS: + // apdu_len = encode_tagged_unsigned(&apdu[0], 1); + // break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + // FIXME: needs an encoding function for bitstring + apdu[0] = 0x85; /* what is following? (this is a bitstring) */ + apdu[1] = 0x06; /* length extension to 6 bytes */ + apdu[2] = 0x05; /* 5 unused bits in the final byte */ + apdu[3] = 0x00; /* none of the first 8 bits are set */ + apdu[4] = 0x09; /* bits 3,0 are set */ + apdu[5] = 0x00; /* none of the 3rd set of bits are set */ + apdu[6] = 0x20; /* bit 5 is set */ + apdu[7] = 0x20; /* bit 5 is set */ + apdu_len = 8; /* bytes in this apdu */ + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + // FIXME: needs an encoding function for bitstring + apdu[0] = 0x84; /* what is following? (this is a bitstring) */ + apdu[1] = 0x06; /* 6 unused bits in the final byte */ + apdu[2] = 0xFF; /* All of the first 8 bits are set */ + apdu[3] = 0xFF; /* All of the second 8 bits are set */ + apdu[4] = 0xC0; /* All of the valid bits are set */ + apdu_len = 5; /* bytes in this apdu */ + break; + case PROP_OBJECT_LIST: + // FIXME: hook into real object list, not just device + // Array element zero is the number of objects in the list + if (array_index == 0) + apdu_len = encode_tagged_unsigned(&apdu[0], 1); + // if no index was specified, then try to encode the entire list + // into one packet. Note that more than likely you will have + // to return an error if the number of encoded objects exceeds + // your maximum APDU size. + else if (array_index == BACNET_ARRAY_ALL) + { + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance); + } + else + { + // the first object in the list is at index=1 + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance); + // FIXME: handle the error case of an index beyond the bounds + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_tagged_unsigned(&apdu[0], + Device_Max_APDU_Length_Accepted()); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = encode_tagged_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_tagged_unsigned(&apdu[0], APDU_Timeout); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_tagged_unsigned(&apdu[0], Number_Of_APDU_Retries); + break; + default: + break; + } + + return apdu_len; +} + + + diff --git a/bacnet-stack/device.h b/bacnet-stack/device.h index 48113b3f..6d3f1293 100644 --- a/bacnet-stack/device.h +++ b/bacnet-stack/device.h @@ -40,6 +40,9 @@ #include "bacdef.h" #include "bacenum.h" +uint32_t Device_Object_Identifier(void); +void Device_Set_Object_Identifier(uint32_t object_id); + BACNET_DEVICE_STATUS Device_System_Status(void); void Device_Set_System_Status(BACNET_DEVICE_STATUS status); @@ -61,11 +64,11 @@ void Device_Set_Application_Software_Version(const char *name); const char *Device_Description(void); void Device_Set_Description(const char *name); +// some stack-centric constant values - no set methods uint8_t Device_Protocol_Version(void); -uint8_t Device_Set_Protocol_Revision(void); - +uint8_t Device_Protocol_Revision(void); uint16_t Device_Max_APDU_Length_Accepted(void); -BACNET_SEGMENTATION Device_Set_Segmentation_Supported(void); +BACNET_SEGMENTATION Device_Segmentation_Supported(void); uint16_t Device_APDU_Timeout(void); void Device_Set_APDU_Timeout(uint16_t timeout); @@ -76,5 +79,10 @@ void Device_Set_Number_Of_APDU_Retries(uint8_t retries); uint8_t Device_Database_Revision(void); void Device_Set_Database_Revision(uint8_t revision); +int Device_Encode_Property_APDU( + uint8_t *apdu, + BACNET_PROPERTY_ID property, + int array_index); + #endif diff --git a/bacnet-stack/ports/linux/main.c b/bacnet-stack/ports/linux/main.c index 8a92ac30..290b21c1 100644 --- a/bacnet-stack/ports/linux/main.c +++ b/bacnet-stack/ports/linux/main.c @@ -2,7 +2,7 @@ // Written by Steve Karg - 2005 - skarg@users.sourceforge.net // Bug fixes, feature requests, and suggestions are welcome -// This is one way to use the BACnet stack under Linux +// This is one way to use the embedded BACnet stack under Linux #include #include @@ -20,10 +20,6 @@ static uint8_t Tx_Buf[MAX_MPDU] = {0}; static uint8_t Rx_Buf[MAX_MPDU] = {0}; -// vendor id assigned by ASHRAE -uint16_t Vendor_Id = 42; -uint32_t Device_Id = 111; - // flag to send an I-Am bool I_Am_Request = true; @@ -120,11 +116,22 @@ void WhoIsHandler( return; } -int main(int argc, char *argv[]) +static void Init_Device_Parameters(void) { - BACNET_ADDRESS src = {0}; // address where message came from - uint16_t pdu_len = 0; + // configure my initial values + Device_Set_Object_Identifier(111); + Device_Set_Vendor_Name("Lithonia Lighting"); + Device_Set_Vendor_Identifier(42); + Device_Set_Model_Name("Simple BACnet Server"); + Device_Set_Firmware_Revision("1.00"); + Device_Set_Application_Software_Version("none"); + Device_Set_Description("Example of a simple BACnet server"); + return; +} + +static void Init_Service_Handlers(void) +{ // custom handlers apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,WhoIsHandler); @@ -222,7 +229,15 @@ int main(int argc, char *argv[]) apdu_set_confirmed_handler( SERVICE_CONFIRMED_REQUEST_KEY, UnrecognizedServiceHandler); +} +int main(int argc, char *argv[]) +{ + BACNET_ADDRESS src = {0}; // address where message came from + uint16_t pdu_len = 0; + + Init_Device_Parameters(); + Init_Service_Handlers(); // init the physical layer if (!ethernet_init("eth0")) return 1; diff --git a/bacnet-stack/reject.c b/bacnet-stack/reject.c index 63d09a68..a1dcd9eb 100644 --- a/bacnet-stack/reject.c +++ b/bacnet-stack/reject.c @@ -208,5 +208,5 @@ int main(void) return 0; } -#endif /* TEST_WHOIS */ +#endif /* TEST_REJECT */ #endif /* TEST */ diff --git a/bacnet-stack/reject.h b/bacnet-stack/reject.h index 4640bf63..99bb76ed 100644 --- a/bacnet-stack/reject.h +++ b/bacnet-stack/reject.h @@ -1,6 +1,6 @@ /*####COPYRIGHTBEGIN#### ------------------------------------------- - Copyright (C) 2004 Steve Karg + Copyright (C) 2005 Steve Karg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -52,7 +52,7 @@ int reject_decode_apdu( uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id, - uint8_t *reject_reason) + uint8_t *reject_reason); #ifdef TEST void testReject(Test * pTest); diff --git a/bacnet-stack/test.sh b/bacnet-stack/test.sh index 016114f5..aa2df00e 100755 --- a/bacnet-stack/test.sh +++ b/bacnet-stack/test.sh @@ -44,3 +44,8 @@ make -f reject.mak ./reject >> test.log make -f reject.mak clean +make -f bacerror.mak clean +make -f bacerror.mak +./bacerror >> test.log +make -f bacerror.mak clean + diff --git a/bacnet-stack/whois.h b/bacnet-stack/whois.h index 7d04afcf..f00371ae 100644 --- a/bacnet-stack/whois.h +++ b/bacnet-stack/whois.h @@ -1,6 +1,6 @@ /*####COPYRIGHTBEGIN#### ------------------------------------------- - Copyright (C) 2004 Steve Karg + Copyright (C) 2005 Steve Karg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -37,7 +37,7 @@ #include #include -// encode I-Am service - use -1 for limit if you want unlimited +// encode service - use -1 for limit if you want unlimited int whois_encode_apdu( uint8_t *apdu, int32_t low_limit,