Routing functionality complete, and working. (Have not done extensive testing, though.)

For building, made it possible to include or exclude the routing functionality with simple changes in the main Makefile and enables in lib/Makefile and include/config.h
The Device object (device.c and .h) was extended to support the virtually routed Devices, with most of the new functions in new file gw_device.c to replace their simpler cousins in device.c.
This commit is contained in:
tbrennan3
2010-10-13 15:59:39 +00:00
parent f934f8add8
commit 48411b8dbf
12 changed files with 754 additions and 46 deletions
+20 -11
View File
@@ -1,6 +1,9 @@
# Main Makefile for BACnet-stack project
# Including "routing" in this list to add routing functionality
all: library readprop writeprop readfile writefile reinit server dcc \
whohas whois ucov timesync epics readpropm mstpcap \
whoisrouter iamrouter initrouter gateway
routing
@echo "utilities are in the bin directory"
clean: lib/Makefile\
@@ -87,17 +90,9 @@ whois: demo/whois/Makefile
mstpcap: demo/mstpcap/Makefile
( cd demo/mstpcap ; make clean all; cp mstpcap ../../bin )
whoisrouter: demo/whoisrouter/Makefile
( cd demo/whoisrouter ; make ; cp bacwir ../../bin )
iamrouter: demo/iamrouter/Makefile
( cd demo/iamrouter ; make ; cp baciamr ../../bin )
initrouter: demo/initrouter/Makefile
( cd demo/initrouter ; make ; cp bacinitr ../../bin )
# Add "ports" to the build, if desired
ports: atmega168 bdk-atxx4-mstp at91sam7s
echo "Built the ports"
@echo "Built the ports"
atmega168: ports/atmega168/Makefile
make -C ports/atmega168 clean all
@@ -108,6 +103,20 @@ at91sam7s: ports/at91sam7s/makefile
bdk-atxx4-mstp: ports/bdk-atxx4-mstp/Makefile
make -C ports/bdk-atxx4-mstp clean all
# Build these demo projects if you want to test routing capability
# Also enable BACROUTE_ENABLE in lib/Makefile and BAC_ROUTING in config.h
routing: whoisrouter iamrouter initrouter gateway
@echo "Built routing demos"
whoisrouter: demo/whoisrouter/Makefile
( cd demo/whoisrouter ; make ; cp bacwir ../../bin )
iamrouter: demo/iamrouter/Makefile
( cd demo/iamrouter ; make ; cp baciamr ../../bin )
initrouter: demo/initrouter/Makefile
( cd demo/initrouter ; make ; cp bacinitr ../../bin )
gateway: demo/gateway/Makefile
( cd demo/gateway ; make ; cp bacgateway ../../bin )
+1 -1
View File
@@ -59,7 +59,7 @@ endif
CFLAGS = -Wall $(DEBUGGING) $(OPTIMIZATION) $(INCLUDES) $(DEFINES)
LFLAGS = -Wl,-Map=$(TARGET).map,$(LIBRARIES) $(LINK_OPTIMIZATION)
SRCS = main.c device.c
SRCS = main.c
OBJS = ${SRCS:.c=.o}
-1
View File
@@ -37,7 +37,6 @@
*/
/* Device configuration definitions. */
#define NUM_DEVICES 3 /* Gateway + two remote devices */
#define FIRST_DEVICE_NUMBER 260001
#define VIRTUAL_DNET 2709 /* your choice of number here */
#define DEV_NAME_BASE "Gateway Demo Device"
+89 -2
View File
@@ -76,13 +76,46 @@ static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
int DNET_list[2] = {
VIRTUAL_DNET, -1 /* Need -1 terminator */
};
/** Initialize the Device Objects and each of the child Object instances.
* @param first_object_instance Set the first (gateway) Device to this
instance number, and subsequent devices to incremented values.
*/
void Devices_Init(
uint32_t first_object_instance )
{
int i;
char nameText[MAX_DEV_NAME_LEN];
char descText[MAX_DEV_DESC_LEN];
/* Gateway Device has already been initialized.
* But give it a better Description. */
Routed_Device_Set_Description(DEV_DESCR_GATEWAY, strlen(DEV_DESCR_GATEWAY));
/* Now initialize the remote Device objects. */
for ( i = 1; i < MAX_NUM_DEVICES; i++ )
{
snprintf( nameText, MAX_DEV_NAME_LEN, "%s %d",
DEV_NAME_BASE, i+1);
snprintf( descText, MAX_DEV_DESC_LEN, "%s %d",
DEV_DESCR_REMOTE, i);
Add_Routed_Device( (first_object_instance+i), nameText, descText );
}
}
/** Initialize the handlers we will utilize.
* @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler
*/
static void Init_Service_Handlers(
uint32_t first_object_instance )
{
Devices_Init( first_object_instance );
Routing_Device_Init( first_object_instance );
/* we need to handle who-is to support dynamic device binding */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has);
@@ -119,6 +152,59 @@ static void Init_Service_Handlers(
handler_device_communication_control);
}
/** Initialize the BACnet Device Addresses for each Device object.
* The gateway has already gotten the normal address (eg, PC's IP for BIP) and
* the remote devices get
* - For BIP, the IP address reversed, and 4th byte equal to index.
* (Eg, 11.22.33.44 for the gateway becomes 44.33.22.01 for the first remote
* device.) This is sure to be unique! The port number stays the same.
* - For MS/TP, [Steve inserts a good idea here]
*/
void Initialize_Device_Addresses( )
{
int i = 0; /* First entry is Gateway Device */
DEVICE_OBJECT_DATA* pDev;
uint16_t myPort;
/* Setup info for the main gateway device first */
#if defined(BACDL_BIP)
struct in_addr *netPtr; /* Lets us cast to this type */
uint8_t *gatewayMac = NULL;
uint32_t myAddr = ntohl( bip_get_addr() );
pDev = Get_Routed_Device_Object( i );
gatewayMac = pDev->bacDevAddr.mac; /* Keep pointer to the main MAC */
memcpy( pDev->bacDevAddr.mac, &myAddr, 4 );
myPort = ntohs( bip_get_port() );
memcpy( &pDev->bacDevAddr.mac[4], &myPort, 2 );
pDev->bacDevAddr.mac_len = 6;
#elif defined(BACDL_MSTP)
/* Todo: */
pDev->bacDevAddr.mac_len = 2;
#else
#error "No support for this Data Link Layer type "
#endif
for (i = 1; i < MAX_NUM_DEVICES; i++ ) {
pDev = Get_Routed_Device_Object( i );
if ( pDev == NULL )
continue;
#if defined(BACDL_BIP)
netPtr = (struct in_addr *) pDev->bacDevAddr.mac;
pDev->bacDevAddr.mac[0] = gatewayMac[3];
pDev->bacDevAddr.mac[1] = gatewayMac[2];
pDev->bacDevAddr.mac[2] = gatewayMac[1];
pDev->bacDevAddr.mac[3] = i;
memcpy( &pDev->bacDevAddr.mac[4], &myPort, 2 );
pDev->bacDevAddr.mac_len = 6;
printf( " - Routed device %d at %s \n", i, inet_ntoa( *netPtr ) );
#elif defined(BACDL_MSTP)
/* Todo: set MS/TP net and port #s */
pDev->bacDevAddr.mac_len = 2;
#endif
}
}
/** Handler registered with atexit() inside main function to, well, cleanup.
* Especially if we don't end normally.
* @see datalink_cleanup
@@ -175,8 +261,9 @@ int main(
first_object_instance, MAX_APDU);
Init_Service_Handlers( first_object_instance );
dlenv_init();
atexit(cleanup);
Devices_Init( first_object_instance );
Initialize_Device_Addresses( );
atexit(cleanup);
// /* initialize vmac table and router device */
// device = vmac_initialize(99, 2001);
+42 -13
View File
@@ -35,6 +35,7 @@
#include "npdu.h"
#include "apdu.h"
#include "handlers.h"
#include "device.h"
#include "client.h"
#include "bactext.h"
#include "debug.h"
@@ -173,6 +174,22 @@ static void network_control_handler(
}
}
/** An APDU pre-handler that makes sure that the subsequent APDU handler call
* operates on the right Device Object(s), as addressed by the destination
* (routing) information.
*
* @note Even when the destination is "routed" to our virtual BACnet network,
* the src information does not need to change to reflect that (as it normally
* would for a routed message) because the reply will be sent from the level
* of the gateway Device.
*
* @param src [in] The BACNET_ADDRESS of the message's source.
* @param dest [in] The BACNET_ADDRESS of the message's destination.
* @param DNET_list [in] List of our reachable downstream BACnet Network numbers.
* Normally just one valid entry; terminated with a -1 value.
* @param apdu [in] The apdu portion of the request, to be processed.
* @param apdu_len [in] The total (remaining) length of the apdu.
*/
static void routed_apdu_handler(
BACNET_ADDRESS * src,
BACNET_ADDRESS * dest,
@@ -180,11 +197,30 @@ static void routed_apdu_handler(
uint8_t * apdu,
uint16_t apdu_len)
{
int dnet = DNET_list[0];
if ((dest->net == dnet) || (dest->net == BACNET_BROADCAST_NETWORK)) {
/* Handle the normal, non-routed variety for right now in development */
int dnet = DNET_list[0]; /* Get the DNET of our virtual network */
int i;
/* First, see if it's for the main Gateway Device, either because
* there's no routing info or else its a BACnet broadcast.
*/
if ( (dest->net == 0 ) || (dest->net == BACNET_BROADCAST_NETWORK)) {
/* Handle like a normal, non-routed access of the Gateway Device.
* But first, make sure our internal access is pointing at
* that Device in our table by telling it "no routing info" : */
Lookup_Routed_Device_Address( 0, 0, NULL );
apdu_handler(src, apdu, apdu_len);
} else {
}
/* Now check for our virtual DNET or BACnet broadcast, and check
* against each of our virtually routed Devices.
* If we get a match, have it handle the APDU.
* For broadcasts, all Devices get a chance at it.
*/
if ((dest->net == dnet) || (dest->net == BACNET_BROADCAST_NETWORK)) {
for ( i = 1; i < MAX_NUM_DEVICES; i++ ) {
if ( Lookup_Routed_Device_Address( i, dest->len, dest->adr ) )
apdu_handler(src, apdu, apdu_len);
}
} else if ( dest->net != 0 ) {
/* We don't know how to reach this one */
Send_Reject_Message_To_Network( src, NETWORK_REJECT_NO_ROUTE, dest->net );
}
@@ -242,15 +278,8 @@ void routing_npdu_handler(
* since only routers can handle it (even if for our DNET) */
}
} else if (apdu_offset <= pdu_len) {
if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) {
/* Handle the normal, non-routed variety */
apdu_handler(src, &pdu[apdu_offset],
(uint16_t) (pdu_len - apdu_offset));
} else {
/* Handle the routed variety differently */
routed_apdu_handler(src, &dest, DNET_list, &pdu[apdu_offset],
(uint16_t) (pdu_len - apdu_offset));
}
routed_apdu_handler(src, &dest, DNET_list, &pdu[apdu_offset],
(uint16_t) (pdu_len - apdu_offset));
}
} else {
/* Should we send NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK? */
+10 -2
View File
@@ -53,7 +53,15 @@ static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
/* target address */
static BACNET_ADDRESS Target_Router_Address;
static BACNET_ROUTER_PORT *Target_Router_Port_List;
/* static BACNET_ROUTER_PORT *Target_Router_Port_List; */
#define VIRTUAL_DNET 2709 /* your choice of number here */
/** The list of DNETs that our router can reach.
* Only one entry since we don't support downstream routers.
*/
int DNET_list[2] = {
VIRTUAL_DNET, -1 /* Need -1 terminator */
};
static bool Error_Detected = false;
@@ -319,7 +327,7 @@ int main(
timeout_seconds = apdu_timeout() / 1000;
/* send the request */
Send_Initialize_Routing_Table(&Target_Router_Address,
Target_Router_Port_List);
DNET_list);
/* loop forever */
for (;;) {
/* increment timer - exit if timed out */
+47 -13
View File
@@ -68,10 +68,14 @@
long int timezone;
#endif
/* forward prototypes */
static int Device_Read_Property_Local(
/* local forward (semi-private) and external prototypes */
int Device_Read_Property_Local(
BACNET_READ_PROPERTY_DATA * rpdata);
static bool Device_Write_Property_Local(
bool Device_Write_Property_Local(
BACNET_WRITE_PROPERTY_DATA * wp_data);
extern int Routed_Device_Read_Property_Local(
BACNET_READ_PROPERTY_DATA * rpdata);
extern bool Routed_Device_Write_Property_Local(
BACNET_WRITE_PROPERTY_DATA * wp_data);
/** Defines the group of object helper functions for any supported Object.
@@ -388,14 +392,6 @@ void Device_Property_Lists(
The properties that are constant can be hard coded
into the read-property encoding. */
/* String Lengths - excluding any nul terminator */
#define MAX_DEV_NAME_LEN 32
#define MAX_DEV_LOC_LEN 64
#define MAX_DEV_MOD_LEN 32
#define MAX_DEV_VER_LEN 16
#define MAX_DEV_DESC_LEN 64
static uint32_t Object_Instance_Number = 260001;
static char My_Object_Name[MAX_DEV_NAME_LEN + 1] = "SimpleServer";
static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
@@ -950,7 +946,7 @@ int tm_isdst Daylight Savings flag.
/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or
BACNET_STATUS_ABORT for abort message */
static int Device_Read_Property_Local(
int Device_Read_Property_Local(
BACNET_READ_PROPERTY_DATA * rpdata)
{
int apdu_len = 0; /* return value */
@@ -1220,7 +1216,7 @@ int Device_Read_Property(
}
/* returns true if successful */
static bool Device_Write_Property_Local(
bool Device_Write_Property_Local(
BACNET_WRITE_PROPERTY_DATA * wp_data)
{
bool status = false; /* return value */
@@ -1484,6 +1480,44 @@ bool DeviceGetRRInfo(
return status;
}
#if BAC_ROUTING
/****************************************************************************
************* BACnet Routing Functionality (Optional) **********************
****************************************************************************
* The supporting functions are located in gw_device.c, except for those
* that need access to local data in this file.
****************************************************************************/
/** Initialize the first of our array of Devices with the main Device's
* information, and then swap out some of the Device object functions and
* replace with ones appropriate for routing.
* @ingroup ObjIntf
* @param first_object_instance Set the first (gateway) Device to this
instance number.
*/
void Routing_Device_Init(
uint32_t first_object_instance )
{
struct object_functions *pDevObject = NULL;
/* First, do the usual Device_Init() functions: */
Device_Init();
/* Initialize with our preset strings */
Add_Routed_Device( first_object_instance, My_Object_Name, Description );
/* Now substitute our routed versions of the main object functions. */
pDevObject = &Object_Table[0];
pDevObject->Object_Index_To_Instance = Routed_Device_Index_To_Instance;
pDevObject->Object_Valid_Instance = Routed_Device_Valid_Object_Instance_Number;
pDevObject->Object_Name = Routed_Device_Name;
pDevObject->Object_Read_Property = Routed_Device_Read_Property_Local;
pDevObject->Object_Write_Property = Routed_Device_Write_Property_Local;
}
#endif /* BAC_ROUTING */
#ifdef TEST
#include <assert.h>
#include <string.h>
+426
View File
@@ -0,0 +1,426 @@
/*
* $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 [Description of this file]. */
#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++;
}
+13
View File
@@ -45,6 +45,19 @@
#endif
#endif
/* Enable the Gateway (Routing) functionality here, if desired. */
#if !defined(BAC_ROUTING)
#define BAC_ROUTING 1
#endif
#if !defined(MAX_NUM_DEVICES)
#if BAC_ROUTING
#define MAX_NUM_DEVICES 3 /* Eg, Gateway + two remote devices */
#else
#define MAX_NUM_DEVICES 1 /* Just the one normal BACnet Device Object */
#endif
#endif
/* Define your processor architecture as
Big Endian (PowerPC,68K,Sparc) or Little Endian (Intel,AVR)
ARM and MIPS can be either - what is your setup? */
+92
View File
@@ -101,6 +101,59 @@ typedef unsigned (
unsigned current_index);
/* String Lengths - excluding any nul terminator */
#define MAX_DEV_NAME_LEN 32
#define MAX_DEV_LOC_LEN 64
#define MAX_DEV_MOD_LEN 32
#define MAX_DEV_VER_LEN 16
#define MAX_DEV_DESC_LEN 64
/** Structure to define the Object Properties common to all Objects. */
typedef struct commonBacObj_s
{
/** The BACnet type of this object (ie, what class is this object from?).
* This property, of type BACnetObjectType, indicates membership in a
* particular object type class. Each inherited class will be of one type.
*/
BACNET_OBJECT_TYPE mObject_Type;
/** The instance number for this class instance. */
uint32_t Object_Instance_Number;
/** Object Name; must be unique.
* This property, of type CharacterString, shall represent a name for
* the object that is unique within the BACnet Device that maintains it.
*/
char Object_Name[MAX_DEV_NAME_LEN];
} COMMON_BAC_OBJECT;
/** Structure to define the Properties of Device Objects which distinguish
* one instance from another.
* This structure only defines fields for properties that are unique to
* a given Device object. The rest may be fixed in device.c or hard-coded
* into the read-property encoding.
* This may be useful for implementations which manage multiple Devices,
* eg, a Gateway.
*/
typedef struct devObj_s
{
/** The BACnet Device Address for this device; ->len depends on DLL type. */
BACNET_ADDRESS bacDevAddr;
/** Structure for the Object Properties common to all Objects. */
COMMON_BAC_OBJECT bacObj;
/** Device Description. */
char Description[MAX_DEV_DESC_LEN];
/** The upcounter that shows if the Device ID or object structure has changed. */
uint32_t Database_Revision;
} DEVICE_OBJECT_DATA;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
@@ -224,7 +277,46 @@ extern "C" {
BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */
RR_PROP_INFO * pInfo); /* Where to put the information */
/* Prototypes for Routing functionality in the Device Object.
* Enable by defining BAC_ROUTING in config.h and including gw_device.c
* in the build (lib/Makefile).
*/
void Routing_Device_Init(
uint32_t first_object_instance );
uint16_t Add_Routed_Device(
uint32_t Object_Instance,
const char * Object_Name,
const char * Description );
DEVICE_OBJECT_DATA * Get_Routed_Device_Object(
int idx );
bool Lookup_Routed_Device_Address(
int idx,
uint8_t address_len,
uint8_t * mac_adress );
uint32_t Routed_Device_Index_To_Instance(
unsigned index);
bool Routed_Device_Valid_Object_Instance_Number(
uint32_t object_id);
char *Routed_Device_Name(
uint32_t object_instance);
uint32_t Routed_Device_Object_Instance_Number(
void);
bool Routed_Device_Set_Object_Instance_Number(
uint32_t object_id);
bool Routed_Device_Set_Object_Name(
const char *name,
size_t length);
bool Routed_Device_Set_Description(
const char *name,
size_t length);
void Routed_Device_Inc_Database_Revision(
void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
+1
View File
@@ -84,4 +84,5 @@ typedef struct object_device_t {
uint32_t Database_Revision;
} OBJECT_DEVICE_T;
#endif
+13 -3
View File
@@ -15,6 +15,9 @@ BACNET_DEFINES=-DPRINT_ENABLED=1 -DBACAPP_ALL -DBACFILE
#BACDL_DEFINE=-DBACDL_ARCNET=1
#BACDL_DEFINE=-DBACDL_MSTP=1
BACDL_DEFINE=-DBACDL_BIP=1
# Enable (or else don't define it) Routing functionality
# (also enable BAC_ROUTING in config.h and "routes" in main Makefile)
BACROUTE_ENABLE=y
DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE)
# directories
@@ -92,7 +95,6 @@ HANDLER_SRC = \
$(BACNET_HANDLER)/txbuf.c \
$(BACNET_HANDLER)/noserv.c \
$(BACNET_HANDLER)/h_npdu.c \
$(BACNET_HANDLER)/h_routed_npdu.c \
$(BACNET_HANDLER)/h_whois.c \
$(BACNET_HANDLER)/h_iam.c \
$(BACNET_HANDLER)/h_rp.c \
@@ -128,7 +130,6 @@ HANDLER_SRC = \
$(BACNET_HANDLER)/s_uevent.c \
$(BACNET_HANDLER)/s_whohas.c \
$(BACNET_HANDLER)/s_whois.c \
$(BACNET_HANDLER)/s_router.c \
$(BACNET_HANDLER)/s_upt.c \
$(BACNET_HANDLER)/s_wp.c
@@ -192,7 +193,16 @@ ifdef BACDL_ALL
PORT_SRC = ${PORT_ALL_SRC}
endif
SRCS = ${CORE_SRC} ${PORT_SRC} ${HANDLER_SRC} ${OBJECT_SRC}
ifdef BACROUTE_ENABLE
ROUTING_SRC = \
$(BACNET_HANDLER)/h_routed_npdu.c \
$(BACNET_HANDLER)/s_router.c \
$(BACNET_OBJECT)/gw_device.c
else
ROUTING_SRC =
endif
SRCS = ${CORE_SRC} ${PORT_SRC} ${HANDLER_SRC} ${OBJECT_SRC} ${ROUTING_SRC}
OBJS = ${SRCS:.c=.o}