diff --git a/bacnet-stack/Makefile b/bacnet-stack/Makefile index 0214289c..cbef375b 100644 --- a/bacnet-stack/Makefile +++ b/bacnet-stack/Makefile @@ -16,6 +16,7 @@ SRCS = ports/linux/main.c \ iam.c \ rp.c \ device.c \ + ai.c \ abort.c \ reject.c \ bacerror.c \ diff --git a/bacnet-stack/bacdcode.c b/bacnet-stack/bacdcode.c index 88dc4c61..3341bce3 100644 --- a/bacnet-stack/bacdcode.c +++ b/bacnet-stack/bacdcode.c @@ -544,6 +544,54 @@ bool decode_is_closing_tag_number(uint8_t * apdu, uint8_t tag_number) return (closing_tag && (my_tag_number == tag_number)); } +// from clause 20.2.3 Encoding of a Boolean Value +// and 20.2.1 General Rules for Encoding BACnet Tags +// returns the number of apdu bytes consumed +int encode_tagged_boolean(uint8_t * apdu, bool boolean_value) +{ + int len = 0; + uint32_t len_value = 0; + + if (boolean_value) + len_value = 1; + + len = encode_tag(&apdu[0], BACNET_APPLICATION_TAG_BOOLEAN, false, + len_value); + + return len; +} + +// context tagged is encoded differently +int encode_context_boolean(uint8_t * apdu, bool boolean_value) +{ + apdu[0] = boolean_value ? 1 : 0; + + return 1; +} + +bool decode_context_boolean(uint8_t * apdu) +{ + bool boolean_value = false; + + if (apdu[0]) + boolean_value = true; + + return boolean_value; +} + +// from clause 20.2.3 Encoding of a Boolean Value +// and 20.2.1 General Rules for Encoding BACnet Tags +// returns the number of apdu bytes consumed +bool decode_boolean(uint32_t len_value) +{ + bool boolean_value = false; + + if (len_value) + boolean_value = true; + + return boolean_value; +} + static uint8_t byte_reverse_bits(uint8_t in_byte) { uint8_t out_byte = 0; diff --git a/bacnet-stack/bacdcode.h b/bacnet-stack/bacdcode.h index cea84a1c..d1db1bab 100644 --- a/bacnet-stack/bacdcode.h +++ b/bacnet-stack/bacdcode.h @@ -66,6 +66,12 @@ bool decode_is_closing_tag_number(uint8_t * apdu, uint8_t tag_number); // returns true if the tag is context specific and matches bool decode_is_context_tag(uint8_t * apdu, uint8_t tag_number); +// from clause 20.2.3 Encoding of a Boolean Value +int encode_tagged_boolean(uint8_t * apdu, bool boolean_value); +bool decode_boolean(uint32_t len_value); +int encode_context_boolean(uint8_t * apdu, bool boolean_value); +bool decode_context_boolean(uint8_t * apdu); + // from clause 20.2.10 Encoding of a Bit String Value void bitstring_init(BACNET_BIT_STRING *bit_string); void bitstring_set_bit(BACNET_BIT_STRING *bit_string, uint8_t bit, bool value); diff --git a/bacnet-stack/bacenum.h b/bacnet-stack/bacenum.h index 72bd0fac..780ae0d7 100644 --- a/bacnet-stack/bacenum.h +++ b/bacnet-stack/bacenum.h @@ -759,6 +759,14 @@ typedef enum MAX_BACNET_SERVICES_SUPPORTED = 40 } BACNET_SERVICES_SUPPORTED; +// Bit String Enumerations +typedef enum { + STATUS_FLAG_IN_ALARM = 0, + STATUS_FLAG_FAULT = 1, + STATUS_FLAG_OVERRIDDEN = 2, + STATUS_FLAG_OUT_OF_SERVICE = 3 +} BACNET_STATUS_FLAGS; + typedef enum { ACKNOWLEDGMENT_FILTER_ALL = 0, ACKNOWLEDGMENT_FILTER_ACKED = 1, diff --git a/bacnet-stack/config.h b/bacnet-stack/config.h index 2f847270..30e6bcfb 100644 --- a/bacnet-stack/config.h +++ b/bacnet-stack/config.h @@ -20,4 +20,6 @@ // requests available. #define MAX_TSM_TRANSACTIONS 16 +#define MAX_ANALOG_INPUTS 32 + #endif diff --git a/bacnet-stack/device.c b/bacnet-stack/device.c index 8ed39786..2b36e882 100644 --- a/bacnet-stack/device.c +++ b/bacnet-stack/device.c @@ -1,36 +1,27 @@ -/*####COPYRIGHTBEGIN#### - ------------------------------------------- - 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 - 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####*/ +/************************************************************************** +* +* Copyright (C) 2005 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 @@ -230,6 +221,7 @@ int Device_Encode_Property_APDU( int32_t array_index) { int apdu_len = 0; // return value + int len = 0; // apdu len intermediate value BACNET_BIT_STRING bit_string; int i = 0; @@ -299,8 +291,6 @@ int Device_Encode_Property_APDU( bitstring_init(&bit_string); for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { - // FIXME: when APDU does automatic reject when service is NULL, - // then add this: // bitstring_set_bit(&bit_string, i, apdu_service_supported(i)); // initialize all the services to not-supported bitstring_set_bit(&bit_string, i, false); @@ -319,28 +309,52 @@ int Device_Encode_Property_APDU( bitstring_set_bit(&bit_string, i, false); } bitstring_set_bit(&bit_string, OBJECT_DEVICE, true); + bitstring_set_bit(&bit_string, OBJECT_ANALOG_INPUT, true); apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string); 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); + apdu_len = encode_tagged_unsigned(&apdu[0], 1 + MAX_ANALOG_INPUTS); // 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, + len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE, Object_Instance_Number); + apdu_len = len; + for (i = 0; i < MAX_ANALOG_INPUTS; i++) + { + // assume next one is the same size as this one + // can we all fit into the APDU? + if ((apdu_len + len) >= MAX_APDU) + { + apdu_len = 0; + break; + } + len = encode_tagged_object_id(&apdu[apdu_len], + OBJECT_ANALOG_INPUT, i); + apdu_len += len; + } } else { // the first object in the list is at index=1 - apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE, - Object_Instance_Number); - // FIXME: handle the error case of an index beyond the bounds + if (array_index == 1) + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + else if (array_index < (2 + MAX_ANALOG_INPUTS)) + { + apdu_len = encode_tagged_object_id(&apdu[0], + OBJECT_ANALOG_INPUT, array_index - 2); + } + else + { + // FIXME: handle case where index is beyond our bounds + } } break; case PROP_MAX_APDU_LENGTH_ACCEPTED: diff --git a/bacnet-stack/ports/linux/main.c b/bacnet-stack/ports/linux/main.c index 2e291d72..25a61e92 100644 --- a/bacnet-stack/ports/linux/main.c +++ b/bacnet-stack/ports/linux/main.c @@ -12,6 +12,7 @@ #include "npdu.h" #include "apdu.h" #include "device.h" +#include "ai.h" #include "rp.h" #include "iam.h" #include "whois.h" @@ -157,8 +158,8 @@ void ReadPropertyHandler( BACNET_PROPERTY_ID object_property; int32_t array_index; BACNET_ADDRESS my_address; + bool send = false; - fprintf(stderr,"Received Read-Property Request!\n"); len = rp_decode_service_request( service_request, service_len, @@ -166,6 +167,15 @@ void ReadPropertyHandler( &object_instance, &object_property, &array_index); + fprintf(stderr,"Received Read-Property Request!\n"); + if (len > 0) + fprintf(stderr,"type=%u instance=%u property=%u index=%d\n", + object_type, + object_instance, + object_property, + array_index); + else + fprintf(stderr,"Unable to decode Read-Property Request!\n"); // prepare a reply ethernet_get_my_address(&my_address); // encode the NPDU portion of the packet @@ -175,18 +185,15 @@ void ReadPropertyHandler( &my_address, false, // true for confirmed messages MESSAGE_PRIORITY_NORMAL); - // bad encoding - send an abort + // bad decoding - send an abort if (len == -1) { pdu_len += abort_encode_apdu( &Tx_Buf[pdu_len], service_data->invoke_id, ABORT_REASON_OTHER); - (void)ethernet_send_pdu( - src, // destination address - &Tx_Buf[0], - pdu_len); // number of bytes of data fprintf(stderr,"Sent Abort!\n"); + send = true; } else if (service_data->segmented_message) { @@ -194,10 +201,8 @@ void ReadPropertyHandler( &Tx_Buf[pdu_len], service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED); - (void)ethernet_send_pdu( - src, // destination address - &Tx_Buf[0], - pdu_len); // number of bytes of data + fprintf(stderr,"Sent Abort!\n"); + send = true; } else { @@ -206,29 +211,40 @@ void ReadPropertyHandler( case OBJECT_DEVICE: // FIXME: probably need a length limitation sent with encode // FIXME: might need to return error codes - len = Device_Encode_Property_APDU( - &Temp_Buf[0], - object_property, - array_index); - if (len > 0) + if (object_instance == Device_Object_Instance_Number()) { - // encode the APDU portion of the packet - rp_data.object_type = object_type; - rp_data.object_instance = object_instance; - rp_data.object_property = object_property; - rp_data.array_index = array_index; - rp_data.application_data = &Temp_Buf[0]; - rp_data.application_data_len = len; - // FIXME: probably need a length limitation sent with encode - pdu_len += rp_ack_encode_apdu( - &Tx_Buf[pdu_len], - service_data->invoke_id, - &rp_data); - (void)ethernet_send_pdu( - src, // destination address - &Tx_Buf[0], - pdu_len); // number of bytes of data - fprintf(stderr,"Sent Read Property Ack!\n"); + len = Device_Encode_Property_APDU( + &Temp_Buf[0], + object_property, + array_index); + if (len > 0) + { + // encode the APDU portion of the packet + rp_data.object_type = object_type; + rp_data.object_instance = object_instance; + rp_data.object_property = object_property; + rp_data.array_index = array_index; + rp_data.application_data = &Temp_Buf[0]; + rp_data.application_data_len = len; + // FIXME: probably need a length limitation sent with encode + pdu_len += rp_ack_encode_apdu( + &Tx_Buf[pdu_len], + service_data->invoke_id, + &rp_data); + fprintf(stderr,"Sent Read Property Ack!\n"); + send = true; + } + else + { + pdu_len += bacerror_encode_apdu( + &Tx_Buf[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_READ_PROPERTY, + ERROR_CLASS_PROPERTY, + ERROR_CODE_UNKNOWN_PROPERTY); + fprintf(stderr,"Sent Unknown Property Error!\n"); + send = true; + } } else { @@ -236,13 +252,59 @@ void ReadPropertyHandler( &Tx_Buf[pdu_len], service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY, - ERROR_CLASS_PROPERTY, - ERROR_CODE_UNKNOWN_PROPERTY); - (void)ethernet_send_pdu( - src, // destination address - &Tx_Buf[0], - pdu_len); // number of bytes of data - fprintf(stderr,"Sent Unknown Property Error!\n"); + ERROR_CLASS_OBJECT, + ERROR_CODE_UNKNOWN_OBJECT); + fprintf(stderr,"Sent Unknown Object Error!\n"); + send = true; + } + break; + case OBJECT_ANALOG_INPUT: + if (object_instance < MAX_ANALOG_INPUTS) + { + len = Analog_Input_Encode_Property_APDU( + &Temp_Buf[0], + object_instance, + object_property, + array_index); + if (len > 0) + { + // encode the APDU portion of the packet + rp_data.object_type = object_type; + rp_data.object_instance = object_instance; + rp_data.object_property = object_property; + rp_data.array_index = array_index; + rp_data.application_data = &Temp_Buf[0]; + rp_data.application_data_len = len; + // FIXME: probably need a length limitation sent with encode + pdu_len += rp_ack_encode_apdu( + &Tx_Buf[pdu_len], + service_data->invoke_id, + &rp_data); + fprintf(stderr,"Sent Read Property Ack!\n"); + send = true; + } + else + { + pdu_len += bacerror_encode_apdu( + &Tx_Buf[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_READ_PROPERTY, + ERROR_CLASS_PROPERTY, + ERROR_CODE_UNKNOWN_PROPERTY); + fprintf(stderr,"Sent Unknown Property Error!\n"); + send = true; + } + } + else + { + pdu_len += bacerror_encode_apdu( + &Tx_Buf[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_READ_PROPERTY, + ERROR_CLASS_OBJECT, + ERROR_CODE_UNKNOWN_OBJECT); + fprintf(stderr,"Sent Unknown Object Error!\n"); + send = true; } break; default: @@ -252,14 +314,18 @@ void ReadPropertyHandler( SERVICE_CONFIRMED_READ_PROPERTY, ERROR_CLASS_OBJECT, ERROR_CODE_UNKNOWN_OBJECT); - (void)ethernet_send_pdu( - src, // destination address - &Tx_Buf[0], - pdu_len); // number of bytes of data fprintf(stderr,"Sent Unknown Object Error!\n"); + send = true; break; } } + if (send) + { + (void)ethernet_send_pdu( + src, // destination address + &Tx_Buf[0], + pdu_len); // number of bytes of data + } return; }