cb243c36a8
* Change MIT license texts to SPDX-License-Identifier SPDX-License-Identifier is much easier to understand and grep than license text so use that instead. * Change GPL exception license texts to SPDX-License-Identifier SPDX-License-Identifier is much easier to understand and grep than license text so use that instead. * Change misc license texts to SPDX-License-Identifier There are some external code in repo which are not licenses as most of the stuff in this repo. We still want every file to have SPDX identifier to easily grep licenses. * Add currently used license files Even though Bacnet-Stack is using SPDX identifiers we still need to give those license files with source. For this reason add all license files to license/ folder. SPDX has also files which would make same thing but this is style which example Linux kernel is using and it is quite clear so I choose that one for now. I choosed not yet bring CC-PDDC as that is not right license for those files. --------- Co-authored-by: Kari Argillander <kari.argillander@fidelix.com>
776 lines
28 KiB
C
776 lines
28 KiB
C
/**************************************************************************
|
|
*
|
|
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
*********************************************************************/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h> /* for memmove */
|
|
#include "bacnet/bacdef.h"
|
|
#include "bacnet/bacdcode.h"
|
|
#include "bacnet/bacstr.h"
|
|
#include "bacnet/bacenum.h"
|
|
#include "bacnet/config.h" /* the custom stuff */
|
|
#include "bacnet/apdu.h"
|
|
#include "bacnet/datalink/dlmstp.h"
|
|
#include "rs485.h"
|
|
#include "bacnet/basic/object/ai.h"
|
|
#include "bacnet/basic/object/av.h"
|
|
#include "bacnet/basic/object/bi.h"
|
|
#include "bacnet/basic/object/bv.h"
|
|
#include "bacnet/rp.h"
|
|
#include "bacnet/wp.h"
|
|
#include "bacnet/dcc.h"
|
|
#include "bacnet/version.h"
|
|
#include "bacnet/basic/object/device.h" /* me */
|
|
|
|
/* 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 = 12345;
|
|
static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
|
|
static uint8_t Database_Revision;
|
|
BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE;
|
|
static const char *Reinit_Password = "filister";
|
|
|
|
/**
|
|
* @brief Sets the ReinitializeDevice password
|
|
*
|
|
* The password shall be a null terminated C string of up to
|
|
* 20 ASCII characters for those devices that require the password.
|
|
*
|
|
* For those devices that do not require a password, set to NULL or
|
|
* point to a zero length C string (null terminated).
|
|
*
|
|
* @param the ReinitializeDevice password; can be NULL or empty string
|
|
*/
|
|
bool Device_Reinitialize_Password_Set(const char *password)
|
|
{
|
|
Reinit_Password = password;
|
|
|
|
return true;
|
|
}
|
|
|
|
/** Commands a Device re-initialization, to a given state.
|
|
* The request's password must match for the operation to succeed.
|
|
* This implementation provides a framework, but doesn't
|
|
* actually *DO* anything.
|
|
* @note You could use a mix of states and passwords to multiple outcomes.
|
|
* @note You probably want to restart *after* the simple ack has been sent
|
|
* from the return handler, so just set a local flag here.
|
|
* @ingroup ObjIntf
|
|
*
|
|
* @param rd_data [in,out] The information from the RD request.
|
|
* On failure, the error class and code will be set.
|
|
* @return True if succeeds (password is correct), else False.
|
|
*/
|
|
bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
|
|
{
|
|
bool status = false;
|
|
bool password_success = false;
|
|
|
|
/* From 16.4.1.1.2 Password
|
|
This optional parameter shall be a CharacterString of up to
|
|
20 characters. For those devices that require the password as a
|
|
protection, the service request shall be denied if the parameter
|
|
is absent or if the password is incorrect. For those devices that
|
|
do not require a password, this parameter shall be ignored.*/
|
|
if (Reinit_Password && strlen(Reinit_Password) > 0) {
|
|
if (characterstring_length(&rd_data->password) > 20) {
|
|
rd_data->error_class = ERROR_CLASS_SERVICES;
|
|
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
|
|
} else if (characterstring_ansi_same(
|
|
&rd_data->password, Reinit_Password)) {
|
|
password_success = true;
|
|
} else {
|
|
rd_data->error_class = ERROR_CLASS_SECURITY;
|
|
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
|
|
}
|
|
} else {
|
|
password_success = true;
|
|
}
|
|
if (password_success) {
|
|
switch (rd_data->state) {
|
|
case BACNET_REINIT_COLDSTART:
|
|
case BACNET_REINIT_WARMSTART:
|
|
dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
|
|
/* note: you probably want to restart *after* the
|
|
simple ack has been sent from the return handler
|
|
so just set a flag from here */
|
|
Reinitialize_State = rd_data->state;
|
|
status = true;
|
|
break;
|
|
case BACNET_REINIT_STARTBACKUP:
|
|
case BACNET_REINIT_ENDBACKUP:
|
|
case BACNET_REINIT_STARTRESTORE:
|
|
case BACNET_REINIT_ENDRESTORE:
|
|
case BACNET_REINIT_ABORTRESTORE:
|
|
if (dcc_communication_disabled()) {
|
|
rd_data->error_class = ERROR_CLASS_SERVICES;
|
|
rd_data->error_code = ERROR_CODE_COMMUNICATION_DISABLED;
|
|
} else {
|
|
rd_data->error_class = ERROR_CLASS_SERVICES;
|
|
rd_data->error_code =
|
|
ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED;
|
|
}
|
|
break;
|
|
default:
|
|
rd_data->error_class = ERROR_CLASS_SERVICES;
|
|
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
BACNET_REINITIALIZED_STATE Device_Reinitialized_State(void)
|
|
{
|
|
return Reinitialize_State;
|
|
}
|
|
|
|
void Device_Init(object_functions_t *object_table)
|
|
{
|
|
(void)object_table;
|
|
Reinitialize_State = BACNET_REINIT_IDLE;
|
|
dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
|
|
/* FIXME: Get the data from the eeprom */
|
|
/* I2C_Read_Block(EEPROM_DEVICE_ADDRESS,
|
|
(char *)&Object_Instance_Number,
|
|
sizeof(Object_Instance_Number),
|
|
EEPROM_BACNET_ID_ADDR); */
|
|
}
|
|
|
|
/* methods to manipulate the data */
|
|
uint32_t Device_Object_Instance_Number(void)
|
|
{
|
|
return Object_Instance_Number;
|
|
}
|
|
|
|
bool Device_Set_Object_Instance_Number(uint32_t object_id)
|
|
{
|
|
bool status = true; /* return value */
|
|
|
|
if (object_id <= BACNET_MAX_INSTANCE) {
|
|
Object_Instance_Number = object_id;
|
|
Database_Revision++;
|
|
/* FIXME: Write the data to the eeprom */
|
|
/* I2C_Write_Block(
|
|
EEPROM_DEVICE_ADDRESS,
|
|
(char *)&Object_Instance_Number,
|
|
sizeof(Object_Instance_Number),
|
|
EEPROM_BACNET_ID_ADDR); */
|
|
} else
|
|
status = false;
|
|
|
|
return status;
|
|
}
|
|
|
|
bool Device_Valid_Object_Instance_Number(uint32_t object_id)
|
|
{
|
|
/* BACnet allows for a wildcard instance number */
|
|
return (Object_Instance_Number == object_id);
|
|
}
|
|
|
|
BACNET_DEVICE_STATUS Device_System_Status(void)
|
|
{
|
|
return System_Status;
|
|
}
|
|
|
|
int Device_Set_System_Status(BACNET_DEVICE_STATUS status, bool local)
|
|
{
|
|
if (status < MAX_DEVICE_STATUS) {
|
|
System_Status = status;
|
|
}
|
|
}
|
|
|
|
uint16_t Device_Vendor_Identifier(void)
|
|
{
|
|
return BACNET_VENDOR_ID;
|
|
}
|
|
|
|
uint8_t Device_Protocol_Version(void)
|
|
{
|
|
return BACNET_PROTOCOL_VERSION;
|
|
}
|
|
|
|
uint8_t Device_Protocol_Revision(void)
|
|
{
|
|
return BACNET_PROTOCOL_REVISION;
|
|
}
|
|
|
|
BACNET_SEGMENTATION Device_Segmentation_Supported(void)
|
|
{
|
|
return SEGMENTATION_NONE;
|
|
}
|
|
|
|
uint32_t Device_Database_Revision(void)
|
|
{
|
|
return Database_Revision;
|
|
}
|
|
|
|
/* Since many network clients depend on the object list */
|
|
/* for discovery, it must be consistent! */
|
|
unsigned Device_Object_List_Count(void)
|
|
{
|
|
unsigned count = 1; /* at least 1 for device object */
|
|
|
|
/* FIXME: add objects as needed */
|
|
count += Binary_Value_Count();
|
|
count += Analog_Input_Count();
|
|
count += Binary_Input_Count();
|
|
count += Analog_Value_Count();
|
|
|
|
return count;
|
|
}
|
|
|
|
/* Since many network clients depend on the object list */
|
|
/* for discovery, it must be consistent! */
|
|
bool Device_Object_List_Identifier(
|
|
uint32_t array_index, BACNET_OBJECT_TYPE *object_type, uint32_t *instance)
|
|
{
|
|
bool status = false;
|
|
uint32_t object_index = 0;
|
|
uint32_t object_count = 0;
|
|
|
|
/* device object */
|
|
if (array_index == 1) {
|
|
*object_type = OBJECT_DEVICE;
|
|
*instance = Object_Instance_Number;
|
|
status = true;
|
|
}
|
|
/* normalize the index since
|
|
we know it is not the previous objects */
|
|
/* array index starts at 1 */
|
|
object_index = array_index - 1;
|
|
/* 1 for the device object */
|
|
object_count = 1;
|
|
/* FIXME: add objects as needed */
|
|
/* binary value objects */
|
|
if (!status) {
|
|
object_index -= object_count;
|
|
object_count = Binary_Value_Count();
|
|
/* is it a valid index for this object? */
|
|
if (object_index < object_count) {
|
|
*object_type = OBJECT_BINARY_VALUE;
|
|
*instance = Binary_Value_Index_To_Instance(object_index);
|
|
status = true;
|
|
}
|
|
}
|
|
/* analog input objects */
|
|
if (!status) {
|
|
/* array index starts at 1, and 1 for the device object */
|
|
object_index -= object_count;
|
|
object_count = Analog_Value_Count();
|
|
if (object_index < object_count) {
|
|
*object_type = OBJECT_ANALOG_VALUE;
|
|
*instance = Analog_Value_Index_To_Instance(object_index);
|
|
status = true;
|
|
}
|
|
}
|
|
/* analog input objects */
|
|
if (!status) {
|
|
/* array index starts at 1, and 1 for the device object */
|
|
object_index -= object_count;
|
|
object_count = Analog_Input_Count();
|
|
if (object_index < object_count) {
|
|
*object_type = OBJECT_ANALOG_INPUT;
|
|
*instance = Analog_Input_Index_To_Instance(object_index);
|
|
status = true;
|
|
}
|
|
}
|
|
/* binary input objects */
|
|
if (!status) {
|
|
/* normalize the index since
|
|
we know it is not the previous objects */
|
|
object_index -= object_count;
|
|
object_count = Binary_Input_Count();
|
|
/* is it a valid index for this object? */
|
|
if (object_index < object_count) {
|
|
*object_type = OBJECT_BINARY_INPUT;
|
|
*instance = Binary_Input_Index_To_Instance(object_index);
|
|
status = true;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @brief Encode a BACnetARRAY property element
|
|
* @param object_instance [in] BACnet network port object instance number
|
|
* @param array_index [in] array index requested:
|
|
* 0 to N for individual array members
|
|
* @param apdu [out] Buffer in which the APDU contents are built, or NULL to
|
|
* return the length of buffer if it had been built
|
|
* @return The length of the apdu encoded or
|
|
* BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX
|
|
*/
|
|
int Device_Object_List_Element_Encode(
|
|
uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu)
|
|
{
|
|
int apdu_len = BACNET_STATUS_ERROR;
|
|
BACNET_OBJECT_TYPE object_type;
|
|
uint32_t instance;
|
|
bool found;
|
|
|
|
if (object_instance == Device_Object_Instance_Number()) {
|
|
/* single element is zero based, add 1 for BACnetARRAY which is one
|
|
* based */
|
|
array_index++;
|
|
found =
|
|
Device_Object_List_Identifier(array_index, &object_type, &instance);
|
|
if (found) {
|
|
apdu_len =
|
|
encode_application_object_id(apdu, object_type, instance);
|
|
}
|
|
}
|
|
|
|
return apdu_len;
|
|
}
|
|
|
|
/* returns true if successful */
|
|
int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
|
|
{
|
|
static char string_buffer[28];
|
|
static BACNET_CHARACTER_STRING char_string;
|
|
int apdu_len = 0; /* return value */
|
|
BACNET_BIT_STRING bit_string;
|
|
uint32_t i = 0;
|
|
uint32_t count = 0;
|
|
BACNET_TIME local_time;
|
|
BACNET_DATE local_date;
|
|
uint8_t year = 0;
|
|
int16_t TimeZone = 0;
|
|
uint8_t *apdu = NULL;
|
|
int apdu_max = 0;
|
|
|
|
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
|
(rpdata->application_data_len == 0)) {
|
|
return 0;
|
|
}
|
|
apdu = rpdata->application_data;
|
|
/* FIXME: change the hardcoded names to suit your application */
|
|
switch (rpdata->object_property) {
|
|
case PROP_OBJECT_IDENTIFIER:
|
|
apdu_len = encode_application_object_id(
|
|
&apdu[0], OBJECT_DEVICE, Object_Instance_Number);
|
|
break;
|
|
case PROP_OBJECT_NAME:
|
|
(void)strcpypgm2ram(&string_buffer[0], "PIC18F6720 Device");
|
|
characterstring_init_ansi(&char_string, string_buffer);
|
|
apdu_len =
|
|
encode_application_character_string(&apdu[0], &char_string);
|
|
break;
|
|
case PROP_OBJECT_TYPE:
|
|
apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE);
|
|
break;
|
|
case PROP_DESCRIPTION:
|
|
(void)strcpypgm2ram(&string_buffer[0], "BACnet Demo");
|
|
characterstring_init_ansi(&char_string, string_buffer);
|
|
apdu_len =
|
|
encode_application_character_string(&apdu[0], &char_string);
|
|
break;
|
|
case PROP_SYSTEM_STATUS:
|
|
apdu_len =
|
|
encode_application_enumerated(&apdu[0], Device_System_Status());
|
|
break;
|
|
case PROP_VENDOR_NAME:
|
|
(void)strcpypgm2ram(&string_buffer[0], BACNET_VENDOR_NAME);
|
|
characterstring_init_ansi(&char_string, string_buffer);
|
|
apdu_len =
|
|
encode_application_character_string(&apdu[0], &char_string);
|
|
break;
|
|
case PROP_VENDOR_IDENTIFIER:
|
|
apdu_len = encode_application_unsigned(
|
|
&apdu[0], Device_Vendor_Identifier());
|
|
break;
|
|
case PROP_MODEL_NAME:
|
|
(void)strcpypgm2ram(&string_buffer[0], "GNU Demo");
|
|
characterstring_init_ansi(&char_string, string_buffer);
|
|
apdu_len =
|
|
encode_application_character_string(&apdu[0], &char_string);
|
|
break;
|
|
case PROP_FIRMWARE_REVISION:
|
|
(void)strcpypgm2ram(&string_buffer[0], BACNET_VERSION_TEXT);
|
|
characterstring_init_ansi(&char_string, string_buffer);
|
|
apdu_len =
|
|
encode_application_character_string(&apdu[0], &char_string);
|
|
break;
|
|
case PROP_APPLICATION_SOFTWARE_VERSION:
|
|
(void)strcpypgm2ram(&string_buffer[0], "1.0");
|
|
characterstring_init_ansi(&char_string, string_buffer);
|
|
apdu_len =
|
|
encode_application_character_string(&apdu[0], &char_string);
|
|
break;
|
|
case PROP_LOCATION:
|
|
(void)strcpypgm2ram(&string_buffer[0], "USA");
|
|
characterstring_init_ansi(&char_string, string_buffer);
|
|
apdu_len =
|
|
encode_application_character_string(&apdu[0], &char_string);
|
|
break;
|
|
case PROP_PROTOCOL_VERSION:
|
|
apdu_len = encode_application_unsigned(
|
|
&apdu[0], Device_Protocol_Version());
|
|
break;
|
|
case PROP_PROTOCOL_REVISION:
|
|
apdu_len = encode_application_unsigned(
|
|
&apdu[0], Device_Protocol_Revision());
|
|
break;
|
|
case PROP_PROTOCOL_SERVICES_SUPPORTED:
|
|
/* Note: list of services that are executed, not initiated. */
|
|
bitstring_init(&bit_string);
|
|
for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) {
|
|
/* automatic lookup based on handlers set */
|
|
bitstring_set_bit(
|
|
&bit_string, (uint8_t)i, apdu_service_supported(i));
|
|
}
|
|
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
|
break;
|
|
case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED:
|
|
/* Note: this is the list of objects that can be in this device,
|
|
not a list of objects that this device can access */
|
|
bitstring_init(&bit_string);
|
|
for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) {
|
|
/* initialize all the object types to not-supported */
|
|
bitstring_set_bit(&bit_string, (uint8_t)i, false);
|
|
}
|
|
/* FIXME: indicate the objects that YOU support */
|
|
bitstring_set_bit(&bit_string, OBJECT_DEVICE, true);
|
|
bitstring_set_bit(&bit_string, OBJECT_ANALOG_VALUE, true);
|
|
bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true);
|
|
bitstring_set_bit(&bit_string, OBJECT_ANALOG_INPUT, true);
|
|
bitstring_set_bit(&bit_string, OBJECT_BINARY_INPUT, true);
|
|
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
|
break;
|
|
case PROP_OBJECT_LIST:
|
|
count = Device_Object_List_Count();
|
|
apdu_len = bacnet_array_encode(rpdata->object_instance,
|
|
rpdata->array_index,
|
|
Device_Object_List_Element_Encode,
|
|
count, apdu, apdu_max);
|
|
if (apdu_len == BACNET_STATUS_ABORT) {
|
|
rpdata->error_code =
|
|
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
|
} else if (apdu_len == BACNET_STATUS_ERROR) {
|
|
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
|
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
|
}
|
|
break;
|
|
case PROP_MAX_APDU_LENGTH_ACCEPTED:
|
|
apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU);
|
|
break;
|
|
case PROP_SEGMENTATION_SUPPORTED:
|
|
apdu_len = encode_application_enumerated(
|
|
&apdu[0], Device_Segmentation_Supported());
|
|
break;
|
|
case PROP_APDU_TIMEOUT:
|
|
apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout());
|
|
break;
|
|
case PROP_NUMBER_OF_APDU_RETRIES:
|
|
apdu_len = encode_application_unsigned(&apdu[0], apdu_retries());
|
|
break;
|
|
case PROP_DEVICE_ADDRESS_BINDING:
|
|
/* FIXME: encode the list here, if it exists */
|
|
break;
|
|
case PROP_DATABASE_REVISION:
|
|
apdu_len = encode_application_unsigned(
|
|
&apdu[0], Device_Database_Revision());
|
|
break;
|
|
case PROP_MAX_INFO_FRAMES:
|
|
apdu_len =
|
|
encode_application_unsigned(&apdu[0], dlmstp_max_info_frames());
|
|
break;
|
|
case PROP_MAX_MASTER:
|
|
apdu_len =
|
|
encode_application_unsigned(&apdu[0], dlmstp_max_master());
|
|
break;
|
|
case PROP_LOCAL_TIME:
|
|
/* FIXME: if you support time */
|
|
local_time.hour = 0;
|
|
local_time.min = 0;
|
|
local_time.sec = 0;
|
|
local_time.hundredths = 0;
|
|
apdu_len = encode_application_time(&apdu[0], &local_time);
|
|
break;
|
|
case PROP_UTC_OFFSET:
|
|
/* Note: BACnet Time Zone is offset of local time and UTC,
|
|
rather than offset of GMT. It is expressed in minutes */
|
|
apdu_len = encode_application_signed(&apdu[0], 5 * 60 /* EST */);
|
|
break;
|
|
case PROP_LOCAL_DATE:
|
|
/* FIXME: if you support date */
|
|
local_date.year = 2006; /* AD */
|
|
local_date.month = 4; /* Jan=1..Dec=12 */
|
|
local_date.day = 11; /* 1..31 */
|
|
local_date.wday = 0; /* 1=Mon..7=Sun */
|
|
apdu_len = encode_application_date(&apdu[0], &local_date);
|
|
break;
|
|
case PROP_DAYLIGHT_SAVINGS_STATUS:
|
|
/* FIXME: if you support time/date */
|
|
apdu_len = encode_application_boolean(&apdu[0], false);
|
|
break;
|
|
case 9600:
|
|
apdu_len =
|
|
encode_application_unsigned(&apdu[0], RS485_Get_Baud_Rate());
|
|
break;
|
|
default:
|
|
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
|
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
|
apdu_len = -1;
|
|
break;
|
|
}
|
|
/* only array properties can have array options */
|
|
if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) &&
|
|
(rpdata->array_index != BACNET_ARRAY_ALL)) {
|
|
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
|
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
|
apdu_len = BACNET_STATUS_ERROR;
|
|
}
|
|
|
|
return apdu_len;
|
|
}
|
|
|
|
int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
|
{
|
|
int apdu_len = BACNET_STATUS_ERROR;
|
|
|
|
/* initialize the default return values */
|
|
rpdata->error_class = ERROR_CLASS_OBJECT;
|
|
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
|
switch (rpdata->object_type) {
|
|
case OBJECT_ANALOG_INPUT:
|
|
if (Analog_Input_Valid_Instance(rpdata->object_instance)) {
|
|
apdu_len = Analog_Input_Read_Property(rpdata);
|
|
}
|
|
break;
|
|
case OBJECT_ANALOG_VALUE:
|
|
if (Analog_Value_Valid_Instance(rpdata->object_instance)) {
|
|
apdu_len = Analog_Value_Read_Property(rpdata);
|
|
}
|
|
break;
|
|
case OBJECT_BINARY_INPUT:
|
|
if (Binary_Input_Valid_Instance(rpdata->object_instance)) {
|
|
apdu_len = Binary_Input_Read_Property(rpdata);
|
|
}
|
|
break;
|
|
case OBJECT_BINARY_VALUE:
|
|
if (Binary_Value_Valid_Instance(rpdata->object_instance)) {
|
|
apdu_len = Binary_Value_Read_Property(rpdata);
|
|
}
|
|
break;
|
|
case OBJECT_DEVICE:
|
|
if (Device_Valid_Object_Instance_Number(rpdata->object_instance)) {
|
|
apdu_len = Device_Read_Property_Local(rpdata);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return apdu_len;
|
|
}
|
|
|
|
bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
|
{
|
|
bool status = false; /* return value */
|
|
int len = 0;
|
|
BACNET_APPLICATION_DATA_VALUE value;
|
|
|
|
if (!Device_Valid_Object_Instance_Number(wp_data->object_instance)) {
|
|
wp_data->error_class = ERROR_CLASS_OBJECT;
|
|
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
|
return false;
|
|
}
|
|
/* decode the some of the request */
|
|
len = bacapp_decode_application_data(
|
|
wp_data->application_data, wp_data->application_data_len, &value);
|
|
/* FIXME: len < application_data_len: more data? */
|
|
if (len < 0) {
|
|
/* error while decoding - a value larger than we can handle */
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
|
return false;
|
|
}
|
|
if ((wp_data->object_property != PROP_OBJECT_LIST) &&
|
|
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
|
/* only array properties can have array options */
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
|
return false;
|
|
}
|
|
switch (wp_data->object_property) {
|
|
case PROP_OBJECT_IDENTIFIER:
|
|
if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) {
|
|
if ((value.type.Object_Id.type == OBJECT_DEVICE) &&
|
|
(Device_Set_Object_Instance_Number(
|
|
value.type.Object_Id.instance))) {
|
|
/* we could send an I-Am broadcast to let the world know */
|
|
status = true;
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
|
}
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
|
}
|
|
break;
|
|
case PROP_MAX_INFO_FRAMES:
|
|
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
|
if (value.type.Unsigned_Int <= 255) {
|
|
dlmstp_set_max_info_frames(value.type.Unsigned_Int);
|
|
status = true;
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
|
}
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
|
}
|
|
break;
|
|
case PROP_MAX_MASTER:
|
|
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
|
if ((value.type.Unsigned_Int > 0) &&
|
|
(value.type.Unsigned_Int <= 127)) {
|
|
dlmstp_set_max_master(value.type.Unsigned_Int);
|
|
status = true;
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
|
}
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
|
}
|
|
break;
|
|
case PROP_OBJECT_NAME:
|
|
if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
|
|
uint8_t encoding;
|
|
size_t len;
|
|
|
|
encoding =
|
|
characterstring_encoding(&value.type.Character_String);
|
|
len = characterstring_length(&value.type.Character_String);
|
|
if (encoding == CHARACTER_ANSI_X34) {
|
|
if (len <= 20) {
|
|
/* FIXME: set the name */
|
|
/* Display_Set_Name(
|
|
characterstring_value(&value.type.Character_String));
|
|
*/
|
|
/* FIXME: All the object names in a device must be
|
|
unique. Disallow setting the Device Object Name to
|
|
any objects in the device. */
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code =
|
|
ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
|
|
}
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code =
|
|
ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED;
|
|
}
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
|
}
|
|
break;
|
|
case 9600:
|
|
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
|
if (value.type.Unsigned_Int > 115200) {
|
|
RS485_Set_Baud_Rate(value.type.Unsigned_Int);
|
|
status = true;
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
|
}
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
|
}
|
|
break;
|
|
case PROP_NUMBER_OF_APDU_RETRIES:
|
|
case PROP_APDU_TIMEOUT:
|
|
case PROP_VENDOR_IDENTIFIER:
|
|
case PROP_SYSTEM_STATUS:
|
|
case PROP_LOCATION:
|
|
case PROP_DESCRIPTION:
|
|
case PROP_MODEL_NAME:
|
|
case PROP_VENDOR_NAME:
|
|
case PROP_FIRMWARE_REVISION:
|
|
case PROP_APPLICATION_SOFTWARE_VERSION:
|
|
case PROP_LOCAL_TIME:
|
|
case PROP_UTC_OFFSET:
|
|
case PROP_LOCAL_DATE:
|
|
case PROP_DAYLIGHT_SAVINGS_STATUS:
|
|
case PROP_PROTOCOL_VERSION:
|
|
case PROP_PROTOCOL_REVISION:
|
|
case PROP_PROTOCOL_SERVICES_SUPPORTED:
|
|
case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED:
|
|
case PROP_OBJECT_LIST:
|
|
case PROP_MAX_APDU_LENGTH_ACCEPTED:
|
|
case PROP_SEGMENTATION_SUPPORTED:
|
|
case PROP_DEVICE_ADDRESS_BINDING:
|
|
case PROP_DATABASE_REVISION:
|
|
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
|
break;
|
|
default:
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
|
{
|
|
bool status = false; /* Ever the pessamist! */
|
|
struct object_functions *pObject = NULL;
|
|
|
|
/* initialize the default return values */
|
|
wp_data->error_class = ERROR_CLASS_OBJECT;
|
|
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
|
switch (wp_data->object_type) {
|
|
case OBJECT_ANALOG_INPUT:
|
|
if (Analog_Input_Valid_Instance(wp_data->object_instance)) {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
|
}
|
|
break;
|
|
case OBJECT_ANALOG_VALUE:
|
|
if (Analog_Value_Valid_Instance(wp_data->object_instance)) {
|
|
status = Analog_Value_Write_Property(wp_data);
|
|
}
|
|
break;
|
|
case OBJECT_BINARY_INPUT:
|
|
if (Binary_Input_Valid_Instance(wp_data->object_instance)) {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
|
}
|
|
break;
|
|
case OBJECT_BINARY_VALUE:
|
|
if (Binary_Value_Valid_Instance(wp_data->object_instance)) {
|
|
status = Binary_Value_Write_Property(wp_data);
|
|
}
|
|
break;
|
|
case OBJECT_DEVICE:
|
|
if (Device_Valid_Object_Instance_Number(wp_data->object_instance)) {
|
|
status = Device_Write_Property_Local(wp_data);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (status);
|
|
}
|