Add multi-device support for BACnet gateway routing (#1279)

* Expanded Object_List to Object_List[MAX_NUM_DEVICES] array to support per-device objects for virtual remote devices
* Added multi-device iteration for COV handler, device timer, and intrinsic reporting
* Added apps/gateway2 demo application
* When MAX_NUM_DEVICES == 1, behavior is identical to the original implementation
* Conditional compilation with macros ensures no impact on non-gateway applications

---------

Signed-off-by: Hyeongjun Kim <hjun1.kim@samsung.com>
Co-authored-by: haemeok-kang <haemeok.kang@samsung.com>
This commit is contained in:
Hyeongjun Kim
2026-03-31 04:16:17 +09:00
committed by GitHub
parent b8dd89b879
commit f408b6eb7f
54 changed files with 2592 additions and 586 deletions
+109 -6
View File
@@ -60,7 +60,14 @@ typedef struct BACnet_COV_Subscription {
#ifndef MAX_COV_SUBCRIPTIONS
#define MAX_COV_SUBCRIPTIONS 128
#endif
static BACNET_COV_SUBSCRIPTION COV_Subscriptions[MAX_COV_SUBCRIPTIONS];
static BACNET_COV_SUBSCRIPTION COV_Subscriptions_List[MAX_NUM_DEVICES]
[MAX_COV_SUBCRIPTIONS];
#ifdef BAC_ROUTING
#define COV_Subscriptions (COV_Subscriptions_List[Routed_Device_Object_Index()])
#else
#define COV_Subscriptions (COV_Subscriptions_List[0])
#endif
#ifndef MAX_COV_ADDRESSES
#define MAX_COV_ADDRESSES 16
#endif
@@ -97,6 +104,32 @@ static void cov_address_remove_unused(void)
unsigned cov_index = 0;
bool found = false;
#ifdef BAC_ROUTING
uint16_t current_dev_id = Routed_Device_Object_Index();
uint16_t dev_id = 0;
for (cov_index = 0; cov_index < MAX_COV_ADDRESSES; cov_index++) {
if (COV_Addresses[cov_index].valid) {
found = false;
for (dev_id = 0; dev_id < Get_Num_Managed_Devices(); dev_id++) {
Set_Routed_Device_Object_Index(dev_id);
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
if ((COV_Subscriptions[index].flag.valid) &&
(COV_Subscriptions[index].dest_index == cov_index)) {
found = true;
break;
}
}
if (found) {
break;
}
}
if (!found) {
COV_Addresses[cov_index].valid = false;
}
}
}
Set_Routed_Device_Object_Index(current_dev_id);
#else
for (cov_index = 0; cov_index < MAX_COV_ADDRESSES; cov_index++) {
if (COV_Addresses[cov_index].valid) {
found = false;
@@ -112,6 +145,7 @@ static void cov_address_remove_unused(void)
}
}
}
#endif
}
/**
@@ -320,7 +354,27 @@ int handler_cov_encode_subscriptions(uint8_t *apdu, int max_apdu)
void handler_cov_init(void)
{
unsigned index = 0;
#ifdef BAC_ROUTING
uint16_t current_dev_id = Routed_Device_Object_Index();
uint16_t dev_id = 0;
for (dev_id = 0; dev_id < MAX_NUM_DEVICES; dev_id++) {
Set_Routed_Device_Object_Index(dev_id);
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
/* initialize with invalid COV address */
COV_Subscriptions[index].flag.valid = false;
COV_Subscriptions[index].dest_index = MAX_COV_ADDRESSES;
COV_Subscriptions[index].subscriberProcessIdentifier = 0;
COV_Subscriptions[index].monitoredObjectIdentifier.type =
OBJECT_ANALOG_INPUT;
COV_Subscriptions[index].monitoredObjectIdentifier.instance = 0;
COV_Subscriptions[index].flag.issueConfirmedNotifications = false;
COV_Subscriptions[index].invokeID = 0;
COV_Subscriptions[index].lifetime = 0;
COV_Subscriptions[index].flag.send_requested = false;
}
}
Set_Routed_Device_Object_Index(current_dev_id);
#else
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
/* initialize with invalid COV address */
COV_Subscriptions[index].flag.valid = false;
@@ -334,6 +388,7 @@ void handler_cov_init(void)
COV_Subscriptions[index].lifetime = 0;
COV_Subscriptions[index].flag.send_requested = false;
}
#endif
for (index = 0; index < MAX_COV_ADDRESSES; index++) {
COV_Addresses[index].valid = false;
}
@@ -587,7 +642,27 @@ void handler_cov_timer_seconds(uint32_t elapsed_seconds)
{
unsigned index = 0;
uint32_t lifetime_seconds = 0;
#ifdef BAC_ROUTING
uint16_t current_dev_id = Routed_Device_Object_Index();
uint16_t dev_id = 0;
if (elapsed_seconds) {
/* handle the subscription timeouts */
for (dev_id = 0; dev_id < Get_Num_Managed_Devices(); dev_id++) {
Set_Routed_Device_Object_Index(dev_id);
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
if (COV_Subscriptions[index].flag.valid) {
lifetime_seconds = COV_Subscriptions[index].lifetime;
if (lifetime_seconds) {
/* only expire COV with definite lifetimes */
cov_lifetime_expiration_handler(
index, elapsed_seconds, lifetime_seconds);
}
}
}
}
}
Set_Routed_Device_Object_Index(current_dev_id);
#else
if (elapsed_seconds) {
/* handle the subscription timeouts */
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
@@ -601,24 +676,38 @@ void handler_cov_timer_seconds(uint32_t elapsed_seconds)
}
}
}
#endif
}
bool handler_cov_fsm(void)
{
static int index = 0;
static int indices[MAX_NUM_DEVICES] = { 0 };
#ifdef BAC_ROUTING
const int dev_id = Routed_Device_Object_Index();
#else
const int dev_id = 0;
#endif
BACNET_OBJECT_TYPE object_type = MAX_BACNET_OBJECT_TYPE;
uint32_t object_instance = 0;
bool status = false;
bool send = false;
BACNET_PROPERTY_VALUE value_list[MAX_COV_PROPERTIES] = { 0 };
/* states for transmitting */
static enum {
typedef enum cov_fsm_state {
COV_STATE_IDLE = 0,
COV_STATE_MARK,
COV_STATE_CLEAR,
COV_STATE_FREE,
COV_STATE_SEND
} cov_task_state = COV_STATE_IDLE;
} cov_fsm_state_t;
static cov_fsm_state_t cov_task_states[MAX_NUM_DEVICES] = {
COV_STATE_IDLE
};
int index = indices[dev_id];
cov_fsm_state_t cov_task_state = cov_task_states[dev_id];
switch (cov_task_state) {
case COV_STATE_IDLE:
@@ -735,12 +824,26 @@ bool handler_cov_fsm(void)
cov_task_state = COV_STATE_IDLE;
break;
}
indices[dev_id] = index;
cov_task_states[dev_id] = cov_task_state;
return (cov_task_state == COV_STATE_IDLE);
}
void handler_cov_task(void)
{
#ifdef BAC_ROUTING
uint16_t current_dev_id = Routed_Device_Object_Index();
uint16_t dev_id = 0;
for (dev_id = 0; dev_id < Get_Num_Managed_Devices(); dev_id++) {
Set_Routed_Device_Object_Index(dev_id);
handler_cov_fsm();
}
Set_Routed_Device_Object_Index(current_dev_id);
#else
handler_cov_fsm();
#endif
}
static bool cov_subscribe(