Added MS/TP datalink option to BACnet basic server example. (#1077)
This commit is contained in:
@@ -1012,6 +1012,7 @@ if(BACNET_STACK_BUILD_APPS)
|
|||||||
src/bacnet/basic/server/bacnet_port.c
|
src/bacnet/basic/server/bacnet_port.c
|
||||||
src/bacnet/basic/server/bacnet_port_ipv4.c
|
src/bacnet/basic/server/bacnet_port_ipv4.c
|
||||||
src/bacnet/basic/server/bacnet_port_ipv6.c
|
src/bacnet/basic/server/bacnet_port_ipv6.c
|
||||||
|
src/bacnet/basic/server/bacnet_port_mstp.c
|
||||||
)
|
)
|
||||||
target_link_libraries(bacbasic PRIVATE ${PROJECT_NAME})
|
target_link_libraries(bacbasic PRIVATE ${PROJECT_NAME})
|
||||||
target_compile_options(bacbasic PRIVATE
|
target_compile_options(bacbasic PRIVATE
|
||||||
|
|||||||
@@ -210,6 +210,10 @@ server:
|
|||||||
server-basic:
|
server-basic:
|
||||||
$(MAKE) LEGACY=true NOTIFY=false -s -C apps $@
|
$(MAKE) LEGACY=true NOTIFY=false -s -C apps $@
|
||||||
|
|
||||||
|
.PHONY: server-basic-mstp
|
||||||
|
server-basic-mstp:
|
||||||
|
$(MAKE) LEGACY=true NOTIFY=false BACDL=mstp -s -C apps server-basic
|
||||||
|
|
||||||
.PHONY: server-client
|
.PHONY: server-client
|
||||||
server-client:
|
server-client:
|
||||||
$(MAKE) LEGACY=true -s -C apps $@
|
$(MAKE) LEGACY=true -s -C apps $@
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ SRC = main.c \
|
|||||||
$(BACNET_SERVER_DIR)/bacnet_port.c \
|
$(BACNET_SERVER_DIR)/bacnet_port.c \
|
||||||
$(BACNET_SERVER_DIR)/bacnet_port_ipv4.c \
|
$(BACNET_SERVER_DIR)/bacnet_port_ipv4.c \
|
||||||
$(BACNET_SERVER_DIR)/bacnet_port_ipv6.c \
|
$(BACNET_SERVER_DIR)/bacnet_port_ipv6.c \
|
||||||
|
$(BACNET_SERVER_DIR)/bacnet_port_mstp.c \
|
||||||
$(BACNET_OBJECT_DIR)/ai.c \
|
$(BACNET_OBJECT_DIR)/ai.c \
|
||||||
$(BACNET_OBJECT_DIR)/ao.c \
|
$(BACNET_OBJECT_DIR)/ao.c \
|
||||||
$(BACNET_OBJECT_DIR)/av.c \
|
$(BACNET_OBJECT_DIR)/av.c \
|
||||||
|
|||||||
+34
-4
@@ -70,6 +70,7 @@ static dlmstp_hook_frame_rx_complete_cb Valid_Frame_Rx_Callback;
|
|||||||
static dlmstp_hook_frame_rx_complete_cb Valid_Frame_Not_For_Us_Rx_Callback;
|
static dlmstp_hook_frame_rx_complete_cb Valid_Frame_Not_For_Us_Rx_Callback;
|
||||||
static dlmstp_hook_frame_rx_complete_cb Invalid_Frame_Rx_Callback;
|
static dlmstp_hook_frame_rx_complete_cb Invalid_Frame_Rx_Callback;
|
||||||
static DLMSTP_STATISTICS DLMSTP_Statistics;
|
static DLMSTP_STATISTICS DLMSTP_Statistics;
|
||||||
|
static bool DLMSTP_Initialized;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Cleanup the MS/TP datalink
|
* @brief Cleanup the MS/TP datalink
|
||||||
@@ -87,6 +88,7 @@ void dlmstp_cleanup(void)
|
|||||||
pthread_mutex_destroy(&Receive_Packet_Mutex);
|
pthread_mutex_destroy(&Receive_Packet_Mutex);
|
||||||
pthread_mutex_destroy(&Master_Done_Mutex);
|
pthread_mutex_destroy(&Master_Done_Mutex);
|
||||||
pthread_mutex_destroy(&Ring_Buffer_Mutex);
|
pthread_mutex_destroy(&Ring_Buffer_Mutex);
|
||||||
|
DLMSTP_Initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1003,6 +1005,27 @@ void dlmstp_silence_reset(void *arg)
|
|||||||
mstimer_set(&Silence_Timer, 0);
|
mstimer_set(&Silence_Timer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configures the interface name
|
||||||
|
* @param ifname = the interface name
|
||||||
|
*/
|
||||||
|
void dlmstp_set_interface(const char *ifname)
|
||||||
|
{
|
||||||
|
/* note: expects a constant char, or char from the heap */
|
||||||
|
if (ifname) {
|
||||||
|
RS485_Set_Interface((char *)ifname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the interface name
|
||||||
|
* @return the interface name
|
||||||
|
*/
|
||||||
|
const char *dlmstp_get_interface(void)
|
||||||
|
{
|
||||||
|
return RS485_Interface();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize this MS/TP datalink
|
* @brief Initialize this MS/TP datalink
|
||||||
* @param ifname user data structure
|
* @param ifname user data structure
|
||||||
@@ -1013,6 +1036,17 @@ bool dlmstp_init(char *ifname)
|
|||||||
pthread_condattr_t attr;
|
pthread_condattr_t attr;
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
|
|
||||||
|
if (DLMSTP_Initialized) {
|
||||||
|
dlmstp_cleanup();
|
||||||
|
RS485_Cleanup();
|
||||||
|
}
|
||||||
|
DLMSTP_Initialized = true;
|
||||||
|
if (ifname) {
|
||||||
|
RS485_Set_Interface(ifname);
|
||||||
|
debug_fprintf(stderr, "MS/TP Interface: %s\n", ifname);
|
||||||
|
} else {
|
||||||
|
ifname = (char *)RS485_Interface();
|
||||||
|
}
|
||||||
pthread_condattr_init(&attr);
|
pthread_condattr_init(&attr);
|
||||||
// TODO use mach_absolute_time() <mach/mach_time.h> for MONOTONIC clock
|
// TODO use mach_absolute_time() <mach/mach_time.h> for MONOTONIC clock
|
||||||
if ((rv = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) != 0) {
|
if ((rv = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) != 0) {
|
||||||
@@ -1054,10 +1088,6 @@ bool dlmstp_init(char *ifname)
|
|||||||
clock_gettime(CLOCK_MONOTONIC, &Clock_Get_Time_Start);
|
clock_gettime(CLOCK_MONOTONIC, &Clock_Get_Time_Start);
|
||||||
/* initialize hardware */
|
/* initialize hardware */
|
||||||
mstimer_set(&Silence_Timer, 0);
|
mstimer_set(&Silence_Timer, 0);
|
||||||
if (ifname) {
|
|
||||||
RS485_Set_Interface(ifname);
|
|
||||||
debug_fprintf(stderr, "MS/TP Interface: %s\n", ifname);
|
|
||||||
}
|
|
||||||
RS485_Initialize();
|
RS485_Initialize();
|
||||||
MSTP_Port.InputBuffer = &RxBuffer[0];
|
MSTP_Port.InputBuffer = &RxBuffer[0];
|
||||||
MSTP_Port.InputBufferSize = sizeof(RxBuffer);
|
MSTP_Port.InputBufferSize = sizeof(RxBuffer);
|
||||||
|
|||||||
+34
-4
@@ -74,6 +74,7 @@ static dlmstp_hook_frame_rx_complete_cb Valid_Frame_Rx_Callback;
|
|||||||
static dlmstp_hook_frame_rx_complete_cb Valid_Frame_Not_For_Us_Rx_Callback;
|
static dlmstp_hook_frame_rx_complete_cb Valid_Frame_Not_For_Us_Rx_Callback;
|
||||||
static dlmstp_hook_frame_rx_complete_cb Invalid_Frame_Rx_Callback;
|
static dlmstp_hook_frame_rx_complete_cb Invalid_Frame_Rx_Callback;
|
||||||
static DLMSTP_STATISTICS DLMSTP_Statistics;
|
static DLMSTP_STATISTICS DLMSTP_Statistics;
|
||||||
|
static bool DLMSTP_Initialized;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Cleanup the MS/TP datalink
|
* @brief Cleanup the MS/TP datalink
|
||||||
@@ -91,6 +92,7 @@ void dlmstp_cleanup(void)
|
|||||||
pthread_mutex_destroy(&Receive_Packet_Mutex);
|
pthread_mutex_destroy(&Receive_Packet_Mutex);
|
||||||
pthread_mutex_destroy(&Master_Done_Mutex);
|
pthread_mutex_destroy(&Master_Done_Mutex);
|
||||||
pthread_mutex_destroy(&Ring_Buffer_Mutex);
|
pthread_mutex_destroy(&Ring_Buffer_Mutex);
|
||||||
|
DLMSTP_Initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1017,6 +1019,27 @@ void dlmstp_silence_reset(void *arg)
|
|||||||
mstimer_set(&Silence_Timer, 0);
|
mstimer_set(&Silence_Timer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configures the interface name
|
||||||
|
* @param ifname = the interface name
|
||||||
|
*/
|
||||||
|
void dlmstp_set_interface(const char *ifname)
|
||||||
|
{
|
||||||
|
/* note: expects a constant char, or char from the heap */
|
||||||
|
if (ifname) {
|
||||||
|
RS485_Set_Interface((char *)ifname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the interface name
|
||||||
|
* @return the interface name
|
||||||
|
*/
|
||||||
|
const char *dlmstp_get_interface(void)
|
||||||
|
{
|
||||||
|
return RS485_Interface();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize this MS/TP datalink
|
* @brief Initialize this MS/TP datalink
|
||||||
* @param ifname user data structure
|
* @param ifname user data structure
|
||||||
@@ -1029,6 +1052,17 @@ bool dlmstp_init(char *ifname)
|
|||||||
pthread_condattr_t attr;
|
pthread_condattr_t attr;
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
|
|
||||||
|
if (DLMSTP_Initialized) {
|
||||||
|
dlmstp_cleanup();
|
||||||
|
RS485_Cleanup();
|
||||||
|
}
|
||||||
|
DLMSTP_Initialized = true;
|
||||||
|
if (ifname) {
|
||||||
|
RS485_Set_Interface(ifname);
|
||||||
|
debug_fprintf(stderr, "MS/TP Interface: %s\n", ifname);
|
||||||
|
} else {
|
||||||
|
ifname = (char *)RS485_Interface();
|
||||||
|
}
|
||||||
pthread_condattr_init(&attr);
|
pthread_condattr_init(&attr);
|
||||||
if ((rv = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) != 0) {
|
if ((rv = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) != 0) {
|
||||||
fprintf(
|
fprintf(
|
||||||
@@ -1070,10 +1104,6 @@ bool dlmstp_init(char *ifname)
|
|||||||
clock_gettime(CLOCK_MONOTONIC, &Clock_Get_Time_Start);
|
clock_gettime(CLOCK_MONOTONIC, &Clock_Get_Time_Start);
|
||||||
/* initialize hardware */
|
/* initialize hardware */
|
||||||
mstimer_set(&Silence_Timer, 0);
|
mstimer_set(&Silence_Timer, 0);
|
||||||
if (ifname) {
|
|
||||||
RS485_Set_Interface(ifname);
|
|
||||||
debug_fprintf(stderr, "MS/TP Interface: %s\n", ifname);
|
|
||||||
}
|
|
||||||
RS485_Initialize();
|
RS485_Initialize();
|
||||||
MSTP_Port.InputBuffer = &RxBuffer[0];
|
MSTP_Port.InputBuffer = &RxBuffer[0];
|
||||||
MSTP_Port.InputBufferSize = sizeof(RxBuffer);
|
MSTP_Port.InputBufferSize = sizeof(RxBuffer);
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
#include "bacnet/basic/server/bacnet_port_ipv4.h"
|
#include "bacnet/basic/server/bacnet_port_ipv4.h"
|
||||||
#elif defined(BACDL_BIP6)
|
#elif defined(BACDL_BIP6)
|
||||||
#include "bacnet/basic/server/bacnet_port_ipv6.h"
|
#include "bacnet/basic/server/bacnet_port_ipv6.h"
|
||||||
|
#elif defined(BACDL_MSTP)
|
||||||
|
#include "bacnet/basic/server/bacnet_port_mstp.h"
|
||||||
#endif
|
#endif
|
||||||
/* me! */
|
/* me! */
|
||||||
#include "bacnet/basic/server/bacnet_port.h"
|
#include "bacnet/basic/server/bacnet_port.h"
|
||||||
@@ -45,6 +47,8 @@ void bacnet_port_task(void)
|
|||||||
bacnet_port_ipv4_task(elapsed_seconds);
|
bacnet_port_ipv4_task(elapsed_seconds);
|
||||||
#elif defined(BACDL_BIP6)
|
#elif defined(BACDL_BIP6)
|
||||||
bacnet_port_ipv6_task(elapsed_seconds);
|
bacnet_port_ipv6_task(elapsed_seconds);
|
||||||
|
#elif defined(BACDL_MSTP)
|
||||||
|
bacnet_port_mstp_task(elapsed_seconds);
|
||||||
#else
|
#else
|
||||||
/* nothing to do */
|
/* nothing to do */
|
||||||
(void)elapsed_seconds;
|
(void)elapsed_seconds;
|
||||||
@@ -64,6 +68,8 @@ bool bacnet_port_init(void)
|
|||||||
status = bacnet_port_ipv4_init();
|
status = bacnet_port_ipv4_init();
|
||||||
#elif defined(BACDL_BIP6)
|
#elif defined(BACDL_BIP6)
|
||||||
status = bacnet_port_ipv6_init();
|
status = bacnet_port_ipv6_init();
|
||||||
|
#elif defined(BACDL_MSTP)
|
||||||
|
status = bacnet_port_mstp_init();
|
||||||
#endif
|
#endif
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief The BACnet datalink tasks for handling the device specific
|
||||||
|
* data link layer
|
||||||
|
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||||
|
* @date January 2025
|
||||||
|
* @copyright SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
/* BACnet definitions */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
/* BACnet library API */
|
||||||
|
#include "bacnet/basic/object/netport.h"
|
||||||
|
#include "bacnet/datalink/datalink.h"
|
||||||
|
#include "bacnet/datalink/dlmstp.h"
|
||||||
|
/* BACnet Basic network port */
|
||||||
|
#include "bacnet/basic/server/bacnet_port_mstp.h"
|
||||||
|
|
||||||
|
#if defined(BACDL_MSTP)
|
||||||
|
static struct dlmstp_statistics Statistics = { 0 };
|
||||||
|
static uint16_t Timer_Seconds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Application Task
|
||||||
|
*/
|
||||||
|
void bacnet_port_mstp_task(uint16_t elapsed_seconds)
|
||||||
|
{
|
||||||
|
Timer_Seconds += elapsed_seconds;
|
||||||
|
if (Timer_Seconds >= 60) {
|
||||||
|
Timer_Seconds = 0;
|
||||||
|
dlmstp_fill_statistics(&Statistics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the network port object.
|
||||||
|
* @return true if successful
|
||||||
|
*/
|
||||||
|
bool bacnet_port_mstp_init(void)
|
||||||
|
{
|
||||||
|
uint32_t instance = 1;
|
||||||
|
BACNET_ADDRESS addr = { 0 };
|
||||||
|
|
||||||
|
if (!dlmstp_init(NULL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Network_Port_Object_Instance_Number_Set(0, instance);
|
||||||
|
Network_Port_Name_Set(instance, "BACnet MS/TP Port");
|
||||||
|
Network_Port_Type_Set(instance, PORT_TYPE_MSTP);
|
||||||
|
dlmstp_get_my_address(&addr);
|
||||||
|
Network_Port_MAC_Address_Set(instance, &addr.mac[0], addr.mac_len);
|
||||||
|
Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED);
|
||||||
|
Network_Port_Out_Of_Service_Set(instance, false);
|
||||||
|
Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN);
|
||||||
|
Network_Port_APDU_Length_Set(instance, MAX_APDU);
|
||||||
|
Network_Port_Network_Number_Set(instance, 0);
|
||||||
|
/* MS/TP specific */
|
||||||
|
Network_Port_Link_Speed_Set(instance, dlmstp_baud_rate());
|
||||||
|
Network_Port_MSTP_Max_Info_Frames_Set(instance, dlmstp_max_info_frames());
|
||||||
|
Network_Port_MSTP_Max_Master_Set(instance, dlmstp_max_master());
|
||||||
|
/* last thing - clear pending changes - we don't want to set these
|
||||||
|
since they are already set */
|
||||||
|
Network_Port_Changes_Pending_Set(instance, false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief The BACnet MS/TP datalink tasks for handling the device specific
|
||||||
|
* data link network port layer
|
||||||
|
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||||
|
* @date January 2025
|
||||||
|
* @copyright SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#ifndef BACNET_PORT_MSTP_H
|
||||||
|
#define BACNET_PORT_MSTP_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
/* BACnet Stack defines - first */
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
#include "bacnet/datalink/dlmstp.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
void bacnet_port_mstp_task(uint16_t elapsed_seconds);
|
||||||
|
bool bacnet_port_mstp_init(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
#endif
|
||||||
@@ -1094,6 +1094,24 @@ void dlmstp_silence_reset(void *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set the MS/TP datalink interface
|
||||||
|
* @param ifname - interface name to set
|
||||||
|
*/
|
||||||
|
void dlmstp_set_interface(const char *ifname)
|
||||||
|
{
|
||||||
|
MSTP_Port = (struct mstp_port_struct_t *)ifname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get the MS/TP datalink intferface name
|
||||||
|
* @return interface name
|
||||||
|
*/
|
||||||
|
const char *dlmstp_get_interface(void)
|
||||||
|
{
|
||||||
|
return (const char *)MSTP_Port;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize this MS/TP datalink
|
* @brief Initialize this MS/TP datalink
|
||||||
* @param ifname user data structure
|
* @param ifname user data structure
|
||||||
@@ -1101,8 +1119,12 @@ void dlmstp_silence_reset(void *arg)
|
|||||||
*/
|
*/
|
||||||
bool dlmstp_init(char *ifname)
|
bool dlmstp_init(char *ifname)
|
||||||
{
|
{
|
||||||
|
bool status = false;
|
||||||
struct dlmstp_user_data_t *user;
|
struct dlmstp_user_data_t *user;
|
||||||
MSTP_Port = (struct mstp_port_struct_t *)ifname;
|
|
||||||
|
if (ifname) {
|
||||||
|
MSTP_Port = (struct mstp_port_struct_t *)ifname;
|
||||||
|
}
|
||||||
if (MSTP_Port) {
|
if (MSTP_Port) {
|
||||||
MSTP_Port->SilenceTimer = dlmstp_silence_milliseconds;
|
MSTP_Port->SilenceTimer = dlmstp_silence_milliseconds;
|
||||||
MSTP_Port->SilenceTimerReset = dlmstp_silence_reset;
|
MSTP_Port->SilenceTimerReset = dlmstp_silence_reset;
|
||||||
@@ -1119,7 +1141,8 @@ bool dlmstp_init(char *ifname)
|
|||||||
MSTP_Init(MSTP_Port);
|
MSTP_Init(MSTP_Port);
|
||||||
user->Initialized = true;
|
user->Initialized = true;
|
||||||
}
|
}
|
||||||
|
status = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return status;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,6 +120,10 @@ extern "C" {
|
|||||||
BACNET_STACK_EXPORT
|
BACNET_STACK_EXPORT
|
||||||
bool dlmstp_init(char *ifname);
|
bool dlmstp_init(char *ifname);
|
||||||
BACNET_STACK_EXPORT
|
BACNET_STACK_EXPORT
|
||||||
|
void dlmstp_set_interface(const char *ifname);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
const char *dlmstp_get_interface(void);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
void dlmstp_reset(void);
|
void dlmstp_reset(void);
|
||||||
BACNET_STACK_EXPORT
|
BACNET_STACK_EXPORT
|
||||||
void dlmstp_cleanup(void);
|
void dlmstp_cleanup(void);
|
||||||
|
|||||||
Reference in New Issue
Block a user