Split dlenv_init in order to use bsc node registration from outside (#1230)

This commit is contained in:
Ingmar Krause
2026-02-17 15:31:47 +01:00
committed by GitHub
parent 6bd2b42334
commit 125e17848a
2 changed files with 263 additions and 145 deletions
+246 -140
View File
@@ -823,22 +823,62 @@ void dlenv_network_port_bsc_init(uint32_t instance)
#endif #endif
} }
void bsc_register_as_node(uint32_t instance) /**
* Check if BACnet/SC hub connection is established.
*
* If no hub URIs (primary or failover) are configured, connection/registration
* is not required and this function returns true, consistent with
* bsc_register_as_node().
*/
bool dlenv_is_bsc_hub_connected(void)
{ {
#if defined(BACDL_BSC) #if defined(BACDL_BSC)
/* if a user has configured BACnet/SC port with primary hub URI, */ const char *primary_uri =
/* wait for a establishing of a connection to BACnet/SC hub at first */ Network_Port_SC_Primary_Hub_URI_char(Network_Port_Instance);
/* to reduce possibility of packet losses. */ const char *failover_uri =
if (Network_Port_SC_Primary_Hub_URI_char(instance)) { Network_Port_SC_Failover_Hub_URI_char(Network_Port_Instance);
if (!primary_uri && !failover_uri) {
/* No hub URIs configured: registration/connection not required. */
return true;
}
return dlenv_hub_connection_status_check(Network_Port_Instance);
#else
return true;
#endif
}
static bool bsc_register_as_node(uint32_t instance, bool wait_until_connected)
{
#if defined(BACDL_BSC)
bool is_connected = false;
const char *primary_uri =
Network_Port_SC_Primary_Hub_URI_char(Network_Port_Instance);
const char *failover_uri =
Network_Port_SC_Failover_Hub_URI_char(Network_Port_Instance);
if (primary_uri || failover_uri) {
debug_printf_stderr("Waiting for a BACnet/SC connection to hub...\n"); debug_printf_stderr("Waiting for a BACnet/SC connection to hub...\n");
while (!dlenv_hub_connection_status_check(instance)) { is_connected = dlenv_hub_connection_status_check(instance);
if (!is_connected) {
do {
bsc_wait(1); bsc_wait(1);
bsc_maintenance_timer(1); bsc_maintenance_timer(1);
is_connected = dlenv_hub_connection_status_check(instance);
} while (wait_until_connected && !is_connected);
} }
if (is_connected) {
debug_printf_stderr("Connected to a BACnet/SC hub!\n"); debug_printf_stderr("Connected to a BACnet/SC hub!\n");
} }
} else {
/* No primary/failover hub URI configured: registration/connection
* not required.
*/
is_connected = true;
}
return is_connected;
#else #else
(void)instance; (void)instance;
(void)wait_until_connected;
return true;
#endif #endif
} }
@@ -900,6 +940,200 @@ void dlenv_maintenance_timer(uint16_t elapsed_seconds)
} }
} }
/** Determine the DataLink port type from Environment variables,
* or else to defaults. In BACDL_MULTIPLE builds, this selection
* is also used to choose the active Datalink_Transport.
*
* @return Detected port type based on environment variables and
* compile-time configuration, which can be PORT_TYPE_BIP,
* PORT_TYPE_BIP6, PORT_TYPE_MSTP, PORT_TYPE_BSC, PORT_TYPE_ETHERNET,
* PORT_TYPE_ARCNET, PORT_TYPE_ZIGBEE, or PORT_TYPE_NON_BACNET.
*/
uint8_t dlenv_get_port_type(void)
{
uint8_t port_type = PORT_TYPE_BIP;
if (getenv("BACNET_DATALINK_DEBUG")) {
dlenv_debug_enable();
}
#if defined(BACDL_MULTIPLE)
char *pEnv = getenv("BACNET_DATALINK");
if (pEnv) {
datalink_set(pEnv);
if (bacnet_stricmp("none", pEnv) == 0) {
port_type = PORT_TYPE_NON_BACNET;
} else if (bacnet_stricmp("bip", pEnv) == 0) {
port_type = PORT_TYPE_BIP;
} else if (bacnet_stricmp("bip6", pEnv) == 0) {
port_type = PORT_TYPE_BIP6;
} else if (bacnet_stricmp("ethernet", pEnv) == 0) {
port_type = PORT_TYPE_ETHERNET;
} else if (bacnet_stricmp("arcnet", pEnv) == 0) {
port_type = PORT_TYPE_ARCNET;
} else if (bacnet_stricmp("mstp", pEnv) == 0) {
port_type = PORT_TYPE_MSTP;
} else if (bacnet_stricmp("bsc", pEnv) == 0) {
port_type = PORT_TYPE_BSC;
}
} else {
#if defined(BACDL_BIP)
datalink_set("bip");
port_type = PORT_TYPE_BIP;
#elif defined(BACDL_BIP6)
datalink_set("bip6");
port_type = PORT_TYPE_BIP6;
#elif defined(BACDL_MSTP)
datalink_set("mstp");
port_type = PORT_TYPE_MSTP;
#elif defined(BACDL_ETHERNET)
datalink_set("ethernet");
port_type = PORT_TYPE_ETHERNET;
#elif defined(BACDL_ARCNET)
datalink_set("arcnet");
port_type = PORT_TYPE_ARCNET;
#elif defined(BACDL_ZIGBEE)
datalink_set("zigbee");
port_type = PORT_TYPE_ZIGBEE;
#elif defined(BACDL_BSC)
datalink_set("bsc");
port_type = PORT_TYPE_BSC;
#else
datalink_set("none");
port_type = PORT_TYPE_NON_BACNET;
#endif
}
#else
/* if we are not compiling with multiple datalinks,
then we are using the only one available */
#if defined(BACDL_BIP)
port_type = PORT_TYPE_BIP;
#elif defined(BACDL_BIP6)
port_type = PORT_TYPE_BIP6;
#elif defined(BACDL_MSTP)
port_type = PORT_TYPE_MSTP;
#elif defined(BACDL_ETHERNET)
port_type = PORT_TYPE_ETHERNET;
#elif defined(BACDL_ARCNET)
port_type = PORT_TYPE_ARCNET;
#elif defined(BACDL_ZIGBEE)
port_type = PORT_TYPE_ZIGBEE;
#elif defined(BACDL_BSC)
port_type = PORT_TYPE_BSC;
#else
port_type = PORT_TYPE_NON_BACNET;
#endif
#endif
return port_type;
}
/**
* Initialize the Datalink configuration according to the given port type
* without registering the devices to the network. This is useful when the
* device registration needs to be delayed or shall be done separately, e.g. in
* a different thread.
*
* @param port_type Port type to initialize, such as PORT_TYPE_BIP,
* PORT_TYPE_BIP6, PORT_TYPE_MSTP, PORT_TYPE_BSC, or other supported PORT_TYPE_*
* values. It should be determined before by calling dlenv_get_port_type()
*/
void dlenv_init_no_device_registration(uint8_t port_type)
{
char *pEnv = NULL;
#if defined(BACFILE)
/* initialize the POSIX file objects */
bacfile_posix_init();
if (Datalink_Debug) {
debug_printf_stderr("POSIX file services initialized.\n");
}
#endif
/* === Initialize the Network Port Object Here === */
Network_Port_Type_Set(Network_Port_Instance, port_type);
switch (port_type) {
case PORT_TYPE_BIP:
dlenv_network_port_bip_init(Network_Port_Instance);
break;
case PORT_TYPE_MSTP:
dlenv_network_port_mstp_init(Network_Port_Instance);
break;
case PORT_TYPE_BIP6:
dlenv_network_port_bip6_init(Network_Port_Instance);
break;
case PORT_TYPE_ZIGBEE:
dlenv_network_port_zigbee_init(Network_Port_Instance);
break;
case PORT_TYPE_BSC:
dlenv_network_port_bsc_init(Network_Port_Instance);
break;
default:
break;
}
pEnv = getenv("BACNET_APDU_TIMEOUT");
if (pEnv) {
apdu_timeout_set((uint16_t)strtol(pEnv, NULL, 0));
} else {
if (port_type == PORT_TYPE_MSTP) {
apdu_timeout_set(60000);
}
}
pEnv = getenv("BACNET_APDU_RETRIES");
if (pEnv) {
apdu_retries_set((uint8_t)strtol(pEnv, NULL, 0));
}
/* === INIT - Initialize the Datalink Here === */
pEnv = getenv("BACNET_IFACE");
if (Datalink_Debug) {
debug_fprintf(stderr, "BACNET_IFACE=%s\n", pEnv ? pEnv : "none");
}
if (!datalink_init(pEnv)) {
exit(1);
}
/* === POST INIT - After the Datalink is Initialized === */
#if (MAX_TSM_TRANSACTIONS)
pEnv = getenv("BACNET_INVOKE_ID");
if (pEnv) {
tsm_invokeID_set((uint8_t)strtol(pEnv, NULL, 0));
}
#endif
}
/**
* Registers a device to the network according to the given port type.
* As prerequisite, the Datalink configuration should have been initialized by
* calling dlenv_init_no_device_registration() with the same port type.
*
* This function is not thread safe. It must be called only after
* dlenv_init_no_device_registration() has completed for the same port type,
* and while no other thread is concurrently using or modifying the datalink
* or Network Port Object state. The call may be made immediately after init
* returns or at a later time (including from a different thread), provided
* these conditions are satisfied.
*
* @param port_type
* The network port type that determines which registration mechanism
* (e.g., BIP, BIP6, BSC) is used. For other port types, no registration
* is performed.
* @param wait_until_connected
* Only used for PORT_TYPE_BSC. If true, block until the
* device is connected/registered; otherwise return immediately.
* @return
* true if the device was successfully registered or registration is not
* required for the given port type; false if registration was attempted
* but failed.
*/
bool dlenv_register_device(uint8_t port_type, bool wait_until_connected)
{
if (port_type == PORT_TYPE_BIP) {
return (bbmd_register_as_foreign_device() >= 0);
} else if (port_type == PORT_TYPE_BIP6) {
return (bbmd6_register_as_foreign_device() >= 0);
} else if (port_type == PORT_TYPE_BSC) {
return bsc_register_as_node(
Network_Port_Instance, wait_until_connected);
}
return true;
}
/** Initialize the DataLink configuration from Environment variables, /** Initialize the DataLink configuration from Environment variables,
* or else to defaults. * or else to defaults.
* @ingroup DataLink * @ingroup DataLink
@@ -975,139 +1209,11 @@ void dlenv_maintenance_timer(uint16_t elapsed_seconds)
*/ */
void dlenv_init(void) void dlenv_init(void)
{ {
char *pEnv = NULL; uint8_t port_type = dlenv_get_port_type();
uint8_t port_type = PORT_TYPE_BIP; dlenv_init_no_device_registration(port_type);
if (!dlenv_register_device(port_type, true)) {
if (getenv("BACNET_DATALINK_DEBUG")) { debug_fprintf(
dlenv_debug_enable(); stderr, "dlenv_init: failed to register device for port type %u\n",
} (unsigned)port_type);
#if defined(BACDL_MULTIPLE)
pEnv = getenv("BACNET_DATALINK");
if (pEnv) {
datalink_set(pEnv);
if (bacnet_stricmp("none", pEnv) == 0) {
port_type = PORT_TYPE_NON_BACNET;
} else if (bacnet_stricmp("bip", pEnv) == 0) {
port_type = PORT_TYPE_BIP;
} else if (bacnet_stricmp("bip6", pEnv) == 0) {
port_type = PORT_TYPE_BIP6;
} else if (bacnet_stricmp("ethernet", pEnv) == 0) {
port_type = PORT_TYPE_ETHERNET;
} else if (bacnet_stricmp("arcnet", pEnv) == 0) {
port_type = PORT_TYPE_ARCNET;
} else if (bacnet_stricmp("mstp", pEnv) == 0) {
port_type = PORT_TYPE_MSTP;
} else if (bacnet_stricmp("bsc", pEnv) == 0) {
port_type = PORT_TYPE_BSC;
}
} else {
#if defined(BACDL_BIP)
datalink_set("bip");
port_type = PORT_TYPE_BIP;
#elif defined(BACDL_BIP6)
datalink_set("bip6");
port_type = PORT_TYPE_BIP6;
#elif defined(BACDL_MSTP)
datalink_set("mstp");
port_type = PORT_TYPE_MSTP;
#elif defined(BACDL_ETHERNET)
datalink_set("ethernet");
port_type = PORT_TYPE_ETHERNET;
#elif defined(BACDL_ARCNET)
datalink_set("arcnet");
port_type = PORT_TYPE_ARCNET;
#elif defined(BACDL_ZIGBEE)
datalink_set("zigbee");
port_type = PORT_TYPE_ZIGBEE;
#elif defined(BACDL_BSC)
datalink_set("bsc");
port_type = PORT_TYPE_BSC;
#else
datalink_set("none");
port_type = PORT_TYPE_NON_BACNET;
#endif
}
#else
/* if we are not compiling with multiple datalinks,
then we are using the only one available */
#if defined(BACDL_BIP)
port_type = PORT_TYPE_BIP;
#elif defined(BACDL_BIP6)
port_type = PORT_TYPE_BIP6;
#elif defined(BACDL_MSTP)
port_type = PORT_TYPE_MSTP;
#elif defined(BACDL_ETHERNET)
port_type = PORT_TYPE_ETHERNET;
#elif defined(BACDL_ARCNET)
port_type = PORT_TYPE_ARCNET;
#elif defined(BACDL_ZIGBEE)
port_type = PORT_TYPE_ZIGBEE;
#elif defined(BACDL_BSC)
port_type = PORT_TYPE_BSC;
#else
port_type = PORT_TYPE_NON_BACNET;
#endif
#endif
#if defined(BACFILE)
/* initialize the POSIX file objects */
bacfile_posix_init();
if (Datalink_Debug) {
debug_printf_stderr("POSIX file services initialized.\n");
}
#endif
/* === Initialize the Network Port Object Here === */
Network_Port_Type_Set(Network_Port_Instance, port_type);
switch (port_type) {
case PORT_TYPE_BIP:
dlenv_network_port_bip_init(Network_Port_Instance);
break;
case PORT_TYPE_MSTP:
dlenv_network_port_mstp_init(Network_Port_Instance);
break;
case PORT_TYPE_BIP6:
dlenv_network_port_bip6_init(Network_Port_Instance);
break;
case PORT_TYPE_ZIGBEE:
dlenv_network_port_zigbee_init(Network_Port_Instance);
break;
case PORT_TYPE_BSC:
dlenv_network_port_bsc_init(Network_Port_Instance);
break;
default:
break;
}
pEnv = getenv("BACNET_APDU_TIMEOUT");
if (pEnv) {
apdu_timeout_set((uint16_t)strtol(pEnv, NULL, 0));
} else {
if (port_type == PORT_TYPE_MSTP) {
apdu_timeout_set(60000);
}
}
pEnv = getenv("BACNET_APDU_RETRIES");
if (pEnv) {
apdu_retries_set((uint8_t)strtol(pEnv, NULL, 0));
}
/* === INIT - Initialize the Datalink Here === */
pEnv = getenv("BACNET_IFACE");
if (Datalink_Debug) {
debug_fprintf(stderr, "BACNET_IFACE=%s\n", pEnv ? pEnv : "none");
}
if (!datalink_init(pEnv)) {
exit(1);
}
/* === POST INIT - After the Datalink is Initialized === */
#if (MAX_TSM_TRANSACTIONS)
pEnv = getenv("BACNET_INVOKE_ID");
if (pEnv) {
tsm_invokeID_set((uint8_t)strtol(pEnv, NULL, 0));
}
#endif
if (port_type == PORT_TYPE_BIP) {
bbmd_register_as_foreign_device();
} else if (port_type == PORT_TYPE_BIP6) {
bbmd6_register_as_foreign_device();
} else if (port_type == PORT_TYPE_BSC) {
bsc_register_as_node(Network_Port_Instance);
} }
} }
+12
View File
@@ -20,6 +20,15 @@
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
BACNET_STACK_EXPORT
uint8_t dlenv_get_port_type(void);
BACNET_STACK_EXPORT
void dlenv_init_no_device_registration(uint8_t port_type);
BACNET_STACK_EXPORT
bool dlenv_register_device(uint8_t port_type, bool wait_until_connected);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
void dlenv_init(void); void dlenv_init(void);
@@ -47,6 +56,9 @@ void dlenv_bbmd_ttl_set(uint16_t ttl_secs);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
int dlenv_bbmd_result(void); int dlenv_bbmd_result(void);
BACNET_STACK_EXPORT
bool dlenv_is_bsc_hub_connected(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */