Files
bacnet_stack/bacnet-stack/demo/object/gw_device.c
T
2010-10-13 19:00:57 +00:00

427 lines
14 KiB
C

/*
* $HeadURL: https://svn.eaton.com/svn/PCM-tk/trunk/PCM-tk/etn_source/tbd/gw_device.c $
*---------------------------------------------------------------------
* $Author: brennant $
* $Date: Oct 13, 2010 $
*---------------------------------------------------------------------
* Copyright (c) 2010 Eaton Corporation, All Rights Reserved.
*
* This software is the confidential and proprietary information of
* Eaton Corporation (Confidential Information). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Eaton Corporation.
*
* EATON CORPORATION MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
* SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. EATON
* CORPORATION SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
* LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS
* SOFTWARE OR ITS DERIVATIVES.
*---------------------------------------------------------------------
*/
/** @file gw_device.c Functions that extend the Device object to support routing. */
#include <stdbool.h>
#include <stdint.h>
#include <string.h> /* for memmove */
#include <time.h> /* for timezone, localtime */
#include "bacdef.h"
#include "bacdcode.h"
#include "bacenum.h"
#include "bacapp.h"
#include "config.h" /* the custom stuff */
#include "apdu.h"
#include "wp.h" /* write property handling */
#include "rp.h" /* read property handling */
#include "version.h"
#include "device.h" /* me */
#include "handlers.h"
#include "datalink.h"
#include "address.h"
/* include the objects */
#include "device.h"
#include "ai.h"
#include "ao.h"
#include "av.h"
#include "bi.h"
#include "bo.h"
#include "bv.h"
#include "lc.h"
#include "lsp.h"
#include "mso.h"
#include "ms-input.h"
#include "trendlog.h"
#if defined(BACFILE)
#include "bacfile.h" /* object list dependency */
#endif
/* os specfic includes */
#include "timer.h"
#if defined(__BORLANDC__)
/* seems to not be defined in time.h as specified by The Open Group */
/* difference from UTC and local standard time */
long int timezone;
#endif
/* local forward and external prototypes */
extern int Device_Read_Property_Local(
BACNET_READ_PROPERTY_DATA * rpdata);
extern bool Device_Write_Property_Local(
BACNET_WRITE_PROPERTY_DATA * wp_data);
int Routed_Device_Read_Property_Local(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Routed_Device_Write_Property_Local(
BACNET_WRITE_PROPERTY_DATA * wp_data);
#if !BAC_ROUTING
#warning This file should not be included in the build unless BAC_ROUTING is enabled.
#endif
/****************************************************************************
************* BACnet Routing Functionality (Optional) **********************
****************************************************************************
* It would be correct to view the routing functionality here as inheriting
* and extending the regular Device Object functionality.
****************************************************************************/
/** Model the gateway as the main Device, with (two) remote
* Devices that are reached via its routing capabilities.
*/
DEVICE_OBJECT_DATA Devices[MAX_NUM_DEVICES];
/** Keep track of the number of managed devices, including the gateway */
uint16_t Num_Managed_Devices = 0;
/** Which Device entry are we currently managing.
* Since we are not using actual class objects here, the best we can do is
* keep this local variable which notes which of the Devices the current
* request is addressing. Should default to 0, the main gateway Device.
*/
uint16_t iCurrent_Device_Idx = 0;
/* void Routing_Device_Init(uint32_t first_object_instance) is
* found in device.c
*/
/** Add a Device to our table of Devices[].
* The first entry must be the gateway device.
* @param Object_Instance [in] Set the new Device to this instance number.
* @param sObject_Name [in] Use this Object Name for the Device.
* @param sDescription [in] Set this Description for the Device.
* @return The index of this instance in the Devices[] array,
* or -1 if there isn't enough room to add this Device.
*/
uint16_t Add_Routed_Device(
uint32_t Object_Instance,
const char * sObject_Name,
const char * sDescription )
{
int i = Num_Managed_Devices;
if ( i < MAX_NUM_DEVICES )
{
DEVICE_OBJECT_DATA *pDev = &Devices[i];
Num_Managed_Devices++;
iCurrent_Device_Idx = i;
pDev->bacObj.mObject_Type = OBJECT_DEVICE;
pDev->bacObj.Object_Instance_Number = Object_Instance;
Routed_Device_Set_Object_Name( sObject_Name, strlen( sObject_Name ));
Routed_Device_Set_Description( sDescription, strlen( sDescription ));
pDev->Database_Revision = 0; /* Reset/Initialize now */
return i;
}
else
return -1;
}
/** Return the Device Object descriptive data for the indicated entry.
* @param idx [in] Index into Devices[] array being requested.
* 0 is for the main, gateway Device entry.
* -1 is a special case meaning "whichever iCurrent_Device_Idx
* is currently set to"
* @return Pointer to the requested Device Object data, or NULL if the idx
* is for an invalid row entry (eg, after the last good Device).
*/
DEVICE_OBJECT_DATA * Get_Routed_Device_Object(
int idx )
{
if ( idx == -1 )
return &Devices[iCurrent_Device_Idx];
else if ( (idx >= 0) && (idx < MAX_NUM_DEVICES) )
return &Devices[idx];
else
return NULL;
}
/** See if the Gateway or Routed Device at the given idx matches
* the given MAC address.
* Has the desirable side-effect of setting iCurrent_Device_Idx to the
* given idx if a match is found, for use in the subsequent routing handling
* functions here.
*
* @param idx [in] Index into Devices[] array being requested.
* 0 is for the main, gateway Device entry.
* @param address_len [in] Length of the mac_adress[] field.
* If 0, then this is a MAC broadcast. Otherwise, size is determined
* by the DLL type (eg, 6 for BIP and 2 for MSTP).
* @param mac_adress [in] The desired MAC address of a Device;
*
* @return True if the MAC addresses match (or the address_len is 0,
* meaning MAC broadcast, so it's an automatic match).
* Else False if no match or invalid idx is given.
*/
bool Lookup_Routed_Device_Address(
int idx,
uint8_t address_len,
uint8_t * mac_adress )
{
bool result = false;
DEVICE_OBJECT_DATA *pDev = &Devices[idx];
int i;
if ( (idx >= 0) && (idx < MAX_NUM_DEVICES) ) {
if ( address_len == 0 ) {
/* Automatic match */
iCurrent_Device_Idx = idx;
result = true;
} else {
for (i = 0; i < address_len; i++) {
if (pDev->bacDevAddr.mac[i] != mac_adress[i])
break;
}
if ( i == address_len ) { /* Success! */
iCurrent_Device_Idx = idx;
result = true;
}
}
}
return result;
}
/* methods to override the normal Device objection functions */
uint32_t Routed_Device_Index_To_Instance(
unsigned index)
{
index = index;
return Devices[iCurrent_Device_Idx].bacObj.Object_Instance_Number;
}
/** See if the requested Object instance matches that for the currently
* indexed Device Object.
* iCurrent_Device_Idx must have been set to point to this Device Object
* before this function is called.
* @param object_id [in] Object ID of the desired Device object.
* If the wildcard value (BACNET_MAX_INSTANCE), always matches.
* @return True if Object ID matches the present Device, else False.
*/
bool Routed_Device_Valid_Object_Instance_Number(
uint32_t object_id)
{
bool bResult = false;
DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx];
/* BACnet allows for a wildcard instance number */
if (object_id == BACNET_MAX_INSTANCE)
bResult = true;
if ( pDev->bacObj.Object_Instance_Number == object_id )
bResult = true;
return bResult;
}
char *Routed_Device_Name(
uint32_t object_instance)
{
DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx];
if (object_instance == pDev->bacObj.Object_Instance_Number) {
return pDev->bacObj.Object_Name;
}
return NULL;
}
/** Manages ReadProperty service for fields which are different for routed
* Devices, or hands off to the default Device RP function for the rest.
* @param rpdata [in] Structure which describes the property to be read.
* @return The length of the apdu encoded, or BACNET_STATUS_ERROR for error or
* BACNET_STATUS_ABORT for abort message.
*/
int Routed_Device_Read_Property_Local(
BACNET_READ_PROPERTY_DATA * rpdata)
{
int apdu_len = 0; /* return value */
BACNET_CHARACTER_STRING char_string;
uint8_t *apdu = NULL;
DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx];
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len =
encode_application_object_id(&apdu[0], OBJECT_DEVICE,
pDev->bacObj.Object_Instance_Number);
break;
case PROP_OBJECT_NAME:
characterstring_init_ansi(&char_string, pDev->bacObj.Object_Name);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(&char_string, pDev->Description);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_DATABASE_REVISION:
apdu_len =
encode_application_unsigned(&apdu[0], pDev->Database_Revision);
break;
default:
apdu_len = Device_Read_Property_Local( rpdata);
break;
}
return ( apdu_len );
}
bool Routed_Device_Write_Property_Local(
BACNET_WRITE_PROPERTY_DATA * wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
/* decode the some of the request */
len =
bacapp_decode_application_data(wp_data->application_data,
wp_data->application_data_len, &value);
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;
}
/* FIXME: len < application_data_len: more data? */
switch (wp_data->object_property) {
case PROP_OBJECT_IDENTIFIER:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_OBJECT_ID,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if ((value.type.Object_Id.type == OBJECT_DEVICE) &&
(Routed_Device_Set_Object_Instance_Number(value.type.
Object_Id.instance))) {
/* FIXME: we could send an I-Am broadcast to let the world know */
} else {
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_OBJECT_NAME:
status =
WPValidateString(&value, MAX_DEV_NAME_LEN, false,
&wp_data->error_class, &wp_data->error_code);
if (status) {
Routed_Device_Set_Object_Name(characterstring_value(&value.
type.Character_String),
characterstring_length(&value.type.Character_String));
}
break;
default:
status = Device_Write_Property_Local( wp_data);
break;
}
return status;
}
/* methods to manipulate the data */
/** Return the Object Instance number for our (single) Device Object.
* This is a key function, widely invoked by the handler code, since
* it provides "our" (ie, local) address.
* @ingroup ObjIntf
* @return The Instance number used in the BACNET_OBJECT_ID for the Device.
*/
uint32_t Routed_Device_Object_Instance_Number(
void)
{
return Devices[iCurrent_Device_Idx].bacObj.Object_Instance_Number;
}
bool Routed_Device_Set_Object_Instance_Number(
uint32_t object_id)
{
bool status = true; /* return value */
if (object_id <= BACNET_MAX_INSTANCE) {
/* Make the change and update the database revision */
Devices[iCurrent_Device_Idx].bacObj.Object_Instance_Number = object_id;
Routed_Device_Inc_Database_Revision();
} else
status = false;
return status;
}
/** Sets the Object Name for a routed Device (or the gateway).
* Uses local variable iCurrent_Device_Idx to know which Device
* is to be updated.
* @param name [in] Text for the new Object Name.
* @param length [in] Length of name[] text.
* @return True if succeed in updating Object Name, else False.
*/
bool Routed_Device_Set_Object_Name(
const char *name,
size_t length)
{
bool status = false; /*return value */
DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx];
if (length < MAX_DEV_NAME_LEN) {
/* Make the change and update the database revision */
memmove(pDev->bacObj.Object_Name, name, length);
pDev->bacObj.Object_Name[length] = 0;
Routed_Device_Inc_Database_Revision();
status = true;
}
return status;
}
bool Routed_Device_Set_Description(
const char *name,
size_t length)
{
bool status = false; /*return value */
DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx];
if (length < MAX_DEV_DESC_LEN) {
memmove(pDev->Description, name, length);
pDev->Description[length] = 0;
status = true;
}
return status;
}
/*
* Shortcut for incrementing database revision as this is potentially
* the most common operation if changing object names and ids is
* implemented.
*/
void Routed_Device_Inc_Database_Revision(
void)
{
DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx];
pDev->Database_Revision++;
}