Feature/bacnet secure connect hub (#818)
* Added BACnet Secure Connect datalink. * Added BACnet/SC hub application --------- Co-authored-by: Kirill Neznamov <kirill.neznamov@dsr-corporation.com> Co-authored-by: Mikhail Antropov <michail.antropov@dsr-corporation.com> Co-authored-by: Ondřej Hruška <ondra@ondrovo.com> Co-authored-by: Patrick Grimm <patrick@lunatiki.de>
This commit is contained in:
+80
-2
@@ -17,12 +17,13 @@
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
#endif
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/access_rule.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacint.h"
|
||||
#include "bacnet/bacreal.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/datetime.h"
|
||||
@@ -30,6 +31,7 @@
|
||||
#include "bacnet/bacaction.h"
|
||||
#include "bacnet/lighting.h"
|
||||
#include "bacnet/hostnport.h"
|
||||
#include "bacnet/secure_connect.h"
|
||||
#include "bacnet/weeklyschedule.h"
|
||||
#include "bacnet/calendar_entry.h"
|
||||
#include "bacnet/special_event.h"
|
||||
@@ -518,6 +520,24 @@ int bacapp_encode_application_data(
|
||||
apdu_len = bacnet_channel_value_type_encode(
|
||||
apdu, &value->type.Channel_Value);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_SECURE_CONNECT)
|
||||
case BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST:
|
||||
apdu_len = bacapp_encode_SCFailedConnectionRequest(
|
||||
apdu, &value->type.SC_Failed_Req);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS:
|
||||
apdu_len = bacapp_encode_SCHubFunctionConnection(
|
||||
apdu, &value->type.SC_Hub_Function_Status);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS:
|
||||
apdu_len = bacapp_encode_SCDirectConnection(
|
||||
apdu, &value->type.SC_Direct_Status);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS:
|
||||
apdu_len = bacapp_encode_SCHubConnection(
|
||||
apdu, &value->type.SC_Hub_Status);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@@ -535,6 +555,7 @@ int bacapp_encode_application_data(
|
||||
* @param len_value_type Count of bytes of given tag
|
||||
* @param value Pointer to the application value structure,
|
||||
* used to store the decoded value to.
|
||||
* @note Decodes only the 13 primitive application data types!
|
||||
*
|
||||
* @return Number of octets consumed (could be zero).
|
||||
* Parameter value->tag set to MAX_BACNET_APPLICATION_TAG when
|
||||
@@ -936,6 +957,10 @@ int bacapp_encode_context_data_value(
|
||||
case BACNET_APPLICATION_TAG_SCALE:
|
||||
case BACNET_APPLICATION_TAG_SHED_LEVEL:
|
||||
case BACNET_APPLICATION_TAG_ACCESS_RULE:
|
||||
case BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST:
|
||||
case BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS:
|
||||
case BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS:
|
||||
case BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS:
|
||||
/* complex data is enclosed in open/close tags */
|
||||
len = encode_opening_tag(apdu, context_tag_number);
|
||||
apdu_len += len;
|
||||
@@ -1316,6 +1341,19 @@ int bacapp_known_property_tag(
|
||||
/* BACnetAccessRule */
|
||||
return BACNET_APPLICATION_TAG_ACCESS_RULE;
|
||||
|
||||
case PROP_SC_FAILED_CONNECTION_REQUESTS:
|
||||
return BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST;
|
||||
|
||||
case PROP_SC_HUB_FUNCTION_CONNECTION_STATUS:
|
||||
return BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS;
|
||||
|
||||
case PROP_SC_DIRECT_CONNECT_CONNECTION_STATUS:
|
||||
return BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS;
|
||||
|
||||
case PROP_SC_PRIMARY_HUB_CONNECTION_STATUS:
|
||||
case PROP_SC_FAILOVER_HUB_CONNECTION_STATUS:
|
||||
return BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
@@ -1625,6 +1663,24 @@ int bacapp_decode_application_tag_value(
|
||||
apdu_len = bacnet_channel_value_decode(
|
||||
apdu, apdu_size, &value->type.Channel_Value);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_SECURE_CONNECT)
|
||||
case BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST:
|
||||
apdu_len = bacapp_decode_SCFailedConnectionRequest(
|
||||
apdu, apdu_size, &value->type.SC_Failed_Req);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS:
|
||||
apdu_len = bacapp_decode_SCHubFunctionConnection(
|
||||
apdu, apdu_size, &value->type.SC_Hub_Function_Status);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS:
|
||||
apdu_len = bacapp_decode_SCDirectConnection(
|
||||
apdu, apdu_size, &value->type.SC_Direct_Status);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS:
|
||||
apdu_len = bacapp_decode_SCHubConnection(
|
||||
apdu, apdu_size, &value->type.SC_Hub_Status);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@@ -3539,6 +3595,28 @@ int bacapp_snprintf_value(
|
||||
str, str_len, &value->type.Object_Property_Reference);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_SECURE_CONNECT)
|
||||
case BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST:
|
||||
ret_val = bacapp_snprintf_SCFailedConnectionRequest(
|
||||
str, str_len, &value->type.SC_Failed_Req);
|
||||
break;
|
||||
|
||||
case BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS:
|
||||
ret_val = bacapp_snprintf_SCHubFunctionConnection(
|
||||
str, str_len, &value->type.SC_Hub_Function_Status);
|
||||
break;
|
||||
|
||||
case BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS:
|
||||
ret_val = bacapp_snprintf_SCDirectConnection(
|
||||
str, str_len, &value->type.SC_Direct_Status);
|
||||
break;
|
||||
|
||||
case BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS:
|
||||
ret_val = bacapp_snprintf_SCHubConnection(
|
||||
str, str_len, &value->type.SC_Hub_Status);
|
||||
break;
|
||||
|
||||
#endif
|
||||
#if defined(BACAPP_DESTINATION)
|
||||
case BACNET_APPLICATION_TAG_DESTINATION:
|
||||
ret_val = bacnet_destination_to_ascii(
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "bacnet/calendar_entry.h"
|
||||
#include "bacnet/special_event.h"
|
||||
#include "bacnet/channel_value.h"
|
||||
#include "bacnet/secure_connect.h"
|
||||
|
||||
#ifndef BACAPP_PRINT_ENABLED
|
||||
#if PRINT_ENABLED
|
||||
@@ -167,6 +168,12 @@ typedef struct BACnet_Application_Data_Value {
|
||||
#endif
|
||||
#if defined(BACAPP_CHANNEL_VALUE)
|
||||
BACNET_CHANNEL_VALUE Channel_Value;
|
||||
#endif
|
||||
#if defined(BACAPP_SECURE_CONNECT)
|
||||
BACNET_SC_FAILED_CONNECTION_REQUEST SC_Failed_Req;
|
||||
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS SC_Hub_Function_Status;
|
||||
BACNET_SC_DIRECT_CONNECTION_STATUS SC_Direct_Status;
|
||||
BACNET_SC_HUB_CONNECTION_STATUS SC_Hub_Status;
|
||||
#endif
|
||||
} type;
|
||||
/* simple linked list if needed */
|
||||
|
||||
+3
-1
@@ -148,7 +148,9 @@ typedef uint32_t BACNET_ARRAY_INDEX;
|
||||
Ethernet = 6 bytes
|
||||
BACnet/IPv4 = 6 bytes
|
||||
LonTalk = 7 bytes
|
||||
BACnet/IPv6 = 3 bytes (VMAC) */
|
||||
BACnet/IPv6 = 3 bytes (VMAC)
|
||||
BACnet/SC = 6 bytes (VMAC)
|
||||
*/
|
||||
#define MAX_MAC_LEN 7
|
||||
|
||||
struct BACnet_Device_Address {
|
||||
|
||||
@@ -1599,6 +1599,14 @@ typedef enum {
|
||||
BACNET_APPLICATION_TAG_XY_COLOR,
|
||||
/* BACnetColorCommand */
|
||||
BACNET_APPLICATION_TAG_COLOR_COMMAND,
|
||||
/* BACNET_SC_FAILED_CONNECTION_REQUEST */
|
||||
BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST,
|
||||
/* BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS */
|
||||
BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS,
|
||||
/* BACNET_SC_DIRECT_CONNECTION_STATUS */
|
||||
BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS,
|
||||
/* BACNET_SC_HUB_CONNECTION_STATUS */
|
||||
BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS,
|
||||
/* BACnetBDTEntry */
|
||||
BACNET_APPLICATION_TAG_BDT_ENTRY,
|
||||
/* BACnetFDTEntry */
|
||||
@@ -2172,6 +2180,10 @@ typedef enum {
|
||||
compilers will allocate adequate sized datatype for enum
|
||||
which is used to store decoding */
|
||||
ERROR_CODE_PROPRIETARY_FIRST = 256,
|
||||
/* some error codes for internal stack usage */
|
||||
ERROR_CODE_ = 65535,
|
||||
ERROR_CODE_DISCARD = 65534,
|
||||
ERROR_CODE_DEFAULT = 65535,
|
||||
ERROR_CODE_PROPRIETARY_LAST = 65535
|
||||
} BACNET_ERROR_CODE;
|
||||
|
||||
@@ -2592,6 +2604,10 @@ typedef enum {
|
||||
PORT_TYPE_NON_BACNET = 8,
|
||||
PORT_TYPE_BIP6 = 9,
|
||||
PORT_TYPE_SERIAL = 10,
|
||||
/* For BACnet/SC network port implementations with
|
||||
a Protocol_Revision 24 and higher, BACnet/SC network ports shall be
|
||||
represented by a Network Port object at the BACNET_APPLICATION
|
||||
protocol level with network type of SECURE_CONNECT. */
|
||||
PORT_TYPE_BSC = 11,
|
||||
/* Enumerated values 0-63 are reserved for definition by ASHRAE.
|
||||
Enumerated values 64-255 may be used by others subject to the
|
||||
@@ -2993,4 +3009,20 @@ typedef enum BACnetAuditOperation {
|
||||
AUDIT_OPERATION_PROPRIETARY_MAX = 63
|
||||
} BACNET_AUDIT_OPERATION;
|
||||
|
||||
typedef enum BACnetSCHubConnectorState {
|
||||
/* FIXME: prefix with typedef name as much as possible */
|
||||
BACNET_SC_HUB_CONNECTOR_STATE_NO_HUB_CONNECTION = 0,
|
||||
BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_PRIMARY = 1,
|
||||
BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_FAILOVER = 2,
|
||||
BACNET_SC_HUB_CONNECTOR_STATE_MAX = 3
|
||||
} BACNET_SC_HUB_CONNECTOR_STATE;
|
||||
|
||||
typedef enum BACnetSCConnectionState {
|
||||
BACNET_SC_CONNECTION_STATE_NOT_CONNECTED = 0,
|
||||
BACNET_SC_CONNECTION_STATE_CONNECTED = 1,
|
||||
BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS = 2,
|
||||
BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT = 3,
|
||||
BACNET_SC_CONNECTION_STATE_MAX = 4
|
||||
} BACNET_SC_CONNECTION_STATE;
|
||||
|
||||
#endif /* end of BACENUM_H */
|
||||
|
||||
@@ -133,6 +133,28 @@ static object_functions_t Object_Table[] = {
|
||||
NULL /* Create */,
|
||||
NULL /* Delete */,
|
||||
NULL /* Timer */ },
|
||||
#endif
|
||||
#if defined(BACFILE)
|
||||
{ OBJECT_FILE,
|
||||
bacfile_init,
|
||||
bacfile_count,
|
||||
bacfile_index_to_instance,
|
||||
bacfile_valid_instance,
|
||||
bacfile_object_name,
|
||||
bacfile_read_property,
|
||||
bacfile_write_property,
|
||||
BACfile_Property_Lists,
|
||||
NULL /* ReadRangeInfo */,
|
||||
NULL /* Iterator */,
|
||||
NULL /* Value_Lists */,
|
||||
NULL /* COV */,
|
||||
NULL /* COV Clear */,
|
||||
NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */,
|
||||
NULL /* Remove_List_Element */,
|
||||
NULL /* Create */,
|
||||
NULL /* Delete */,
|
||||
NULL /* Timer */ },
|
||||
#endif
|
||||
{ MAX_BACNET_OBJECT_TYPE,
|
||||
NULL /* Init */,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "bacnet/basic/object/device.h"
|
||||
/* me */
|
||||
#include "bacnet/basic/object/netport.h"
|
||||
#include <bacnet/basic/object/netport_internal.h>
|
||||
|
||||
#if defined(BACDL_BIP6)
|
||||
#include "bacnet/datalink/bvlc6.h"
|
||||
@@ -92,6 +93,11 @@ struct mstp_port {
|
||||
uint8_t Max_Info_Frames;
|
||||
};
|
||||
|
||||
struct bsc_port {
|
||||
uint8_t MAC_Address[6];
|
||||
BACNET_SC_PARAMS Parameters;
|
||||
};
|
||||
|
||||
struct object_data {
|
||||
uint32_t Instance_Number;
|
||||
const char *Object_Name;
|
||||
@@ -111,11 +117,14 @@ struct object_data {
|
||||
struct bacnet_ipv6_port IPv6;
|
||||
struct ethernet_port Ethernet;
|
||||
struct mstp_port MSTP;
|
||||
struct bsc_port BSC;
|
||||
} Network;
|
||||
};
|
||||
|
||||
#ifndef BACNET_NETWORK_PORTS_MAX
|
||||
#define BACNET_NETWORK_PORTS_MAX 1
|
||||
#endif
|
||||
|
||||
static struct object_data Object_List[BACNET_NETWORK_PORTS_MAX];
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
@@ -194,6 +203,53 @@ static const int BIP6_Port_Properties_Optional[] = {
|
||||
-1
|
||||
};
|
||||
|
||||
static const int BSC_Port_Properties_Optional[] = {
|
||||
PROP_NETWORK_NUMBER,
|
||||
PROP_NETWORK_NUMBER_QUALITY,
|
||||
PROP_APDU_LENGTH,
|
||||
PROP_MAC_ADDRESS,
|
||||
PROP_BACNET_IP_MODE,
|
||||
PROP_IP_ADDRESS,
|
||||
PROP_BACNET_IP_UDP_PORT,
|
||||
PROP_IP_SUBNET_MASK,
|
||||
PROP_IP_DEFAULT_GATEWAY,
|
||||
PROP_IP_DNS_SERVER,
|
||||
PROP_MAX_BVLC_LENGTH_ACCEPTED,
|
||||
PROP_MAX_NPDU_LENGTH_ACCEPTED,
|
||||
PROP_SC_PRIMARY_HUB_URI,
|
||||
PROP_SC_FAILOVER_HUB_URI,
|
||||
PROP_SC_MINIMUM_RECONNECT_TIME,
|
||||
PROP_SC_MAXIMUM_RECONNECT_TIME,
|
||||
PROP_SC_CONNECT_WAIT_TIMEOUT,
|
||||
PROP_SC_DISCONNECT_WAIT_TIMEOUT,
|
||||
PROP_SC_HEARTBEAT_TIMEOUT,
|
||||
PROP_SC_HUB_CONNECTOR_STATE,
|
||||
PROP_OPERATIONAL_CERTIFICATE_FILE,
|
||||
PROP_ISSUER_CERTIFICATE_FILES,
|
||||
PROP_CERTIFICATE_SIGNING_REQUEST_FILE,
|
||||
/*SC optional*/
|
||||
#ifdef BACNET_SECURE_CONNECT_ROUTING_TABLE
|
||||
PROP_ROUTING_TABLE,
|
||||
#endif /* BACNET_SECURE_CONNECT_ROUTING_TABLE */
|
||||
#if BSC_CONF_HUB_FUNCTIONS_NUM != 0
|
||||
PROP_SC_PRIMARY_HUB_CONNECTION_STATUS,
|
||||
PROP_SC_FAILOVER_HUB_CONNECTION_STATUS,
|
||||
PROP_SC_HUB_FUNCTION_ENABLE,
|
||||
PROP_SC_HUB_FUNCTION_ACCEPT_URIS,
|
||||
PROP_SC_HUB_FUNCTION_BINDING,
|
||||
PROP_SC_HUB_FUNCTION_CONNECTION_STATUS,
|
||||
#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */
|
||||
#if BSC_CONF_HUB_CONNECTORS_NUM != 0
|
||||
PROP_SC_DIRECT_CONNECT_INITIATE_ENABLE,
|
||||
PROP_SC_DIRECT_CONNECT_ACCEPT_ENABLE,
|
||||
PROP_SC_DIRECT_CONNECT_ACCEPT_URIS,
|
||||
PROP_SC_DIRECT_CONNECT_BINDING,
|
||||
PROP_SC_DIRECT_CONNECT_CONNECTION_STATUS,
|
||||
#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */
|
||||
PROP_SC_FAILED_CONNECTION_REQUESTS,
|
||||
-1
|
||||
};
|
||||
|
||||
static const int Network_Port_Properties_Proprietary[] = { -1 };
|
||||
|
||||
/**
|
||||
@@ -229,6 +285,9 @@ void Network_Port_Property_List(
|
||||
case PORT_TYPE_BIP:
|
||||
*pOptional = BIP_Port_Properties_Optional;
|
||||
break;
|
||||
case PORT_TYPE_BSC:
|
||||
*pOptional = BSC_Port_Properties_Optional;
|
||||
break;
|
||||
case PORT_TYPE_BIP6:
|
||||
*pOptional = BIP6_Port_Properties_Optional;
|
||||
break;
|
||||
@@ -590,6 +649,16 @@ uint8_t Network_Port_Type(uint32_t object_instance)
|
||||
index = Network_Port_Instance_To_Index(object_instance);
|
||||
if (index < BACNET_NETWORK_PORTS_MAX) {
|
||||
port_type = Object_List[index].Network_Type;
|
||||
#if (BACNET_PROTOCOL_REVISION >= 17) && (BACNET_PROTOCOL_REVISION <= 23)
|
||||
/* For BACnet/SC network port implementations with
|
||||
a protocol revision Protocol_Revision 17 and higher through 23,
|
||||
BACnet/SC network ports shall be represented by a Network Port
|
||||
object at the BACNET_APPLICATION protocol level with
|
||||
a proprietary network type value. */
|
||||
if (port_type == PORT_TYPE_BSC) {
|
||||
port_type = PORT_TYPE_BSC_INTERIM;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return port_type;
|
||||
@@ -612,7 +681,18 @@ bool Network_Port_Type_Set(uint32_t object_instance, uint8_t value)
|
||||
|
||||
index = Network_Port_Instance_To_Index(object_instance);
|
||||
if (index < BACNET_NETWORK_PORTS_MAX) {
|
||||
#if (BACNET_PROTOCOL_REVISION >= 17) && (BACNET_PROTOCOL_REVISION <= 23)
|
||||
/* For BACnet/SC network port implementations with
|
||||
a protocol revision Protocol_Revision 17 and higher through 23,
|
||||
BACnet/SC network ports shall be represented by a Network Port
|
||||
object at the BACNET_APPLICATION protocol level with
|
||||
a proprietary network type value. */
|
||||
if (value == PORT_TYPE_BSC_INTERIM) {
|
||||
value = PORT_TYPE_BSC;
|
||||
}
|
||||
#endif
|
||||
Object_List[index].Network_Type = value;
|
||||
|
||||
status = true;
|
||||
}
|
||||
|
||||
@@ -748,6 +828,10 @@ uint8_t Network_Port_MAC_Address_Value(
|
||||
mac = &Object_List[index].Network.IPv6.MAC_Address[0];
|
||||
mac_len = sizeof(Object_List[index].Network.IPv6.MAC_Address);
|
||||
break;
|
||||
case PORT_TYPE_BSC:
|
||||
mac = &Object_List[index].Network.BSC.MAC_Address[0];
|
||||
mac_len = sizeof(Object_List[index].Network.BSC.MAC_Address);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -824,6 +908,10 @@ bool Network_Port_MAC_Address_Set(
|
||||
mac_dest = &Object_List[index].Network.IPv6.MAC_Address[0];
|
||||
mac_size = sizeof(Object_List[index].Network.IPv6.MAC_Address);
|
||||
break;
|
||||
case PORT_TYPE_BSC:
|
||||
mac_dest = &Object_List[index].Network.BSC.MAC_Address[0];
|
||||
mac_size = sizeof(Object_List[index].Network.BSC.MAC_Address);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -3540,6 +3628,178 @@ int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
#ifdef BACDL_BSC
|
||||
case PROP_MAX_BVLC_LENGTH_ACCEPTED:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0],
|
||||
Network_Port_Max_BVLC_Length_Accepted(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_MAX_NPDU_LENGTH_ACCEPTED:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0],
|
||||
Network_Port_Max_NPDU_Length_Accepted(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_SC_PRIMARY_HUB_URI:
|
||||
Network_Port_SC_Primary_Hub_URI(
|
||||
rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_SC_FAILOVER_HUB_URI:
|
||||
Network_Port_SC_Failover_Hub_URI(
|
||||
rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_SC_MINIMUM_RECONNECT_TIME:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0],
|
||||
Network_Port_SC_Minimum_Reconnect_Time(
|
||||
rpdata->object_instance));
|
||||
break;
|
||||
case PROP_SC_MAXIMUM_RECONNECT_TIME:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0],
|
||||
Network_Port_SC_Maximum_Reconnect_Time(
|
||||
rpdata->object_instance));
|
||||
break;
|
||||
case PROP_SC_CONNECT_WAIT_TIMEOUT:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0],
|
||||
Network_Port_SC_Connect_Wait_Timeout(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_SC_DISCONNECT_WAIT_TIMEOUT:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0],
|
||||
Network_Port_SC_Disconnect_Wait_Timeout(
|
||||
rpdata->object_instance));
|
||||
break;
|
||||
case PROP_SC_HEARTBEAT_TIMEOUT:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0],
|
||||
Network_Port_SC_Heartbeat_Timeout(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_SC_HUB_CONNECTOR_STATE:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0],
|
||||
Network_Port_SC_Hub_Connector_State(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_OPERATIONAL_CERTIFICATE_FILE:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0],
|
||||
Network_Port_Operational_Certificate_File(
|
||||
rpdata->object_instance));
|
||||
break;
|
||||
case PROP_ISSUER_CERTIFICATE_FILES:
|
||||
apdu_len = bacnet_array_encode(
|
||||
rpdata->object_instance, rpdata->array_index,
|
||||
Network_Port_Issuer_Certificate_File_Encode,
|
||||
BACNET_ISSUER_CERT_FILE_MAX, apdu, apdu_size);
|
||||
if (apdu_len == BACNET_STATUS_ABORT) {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
} else if (apdu_len == BACNET_STATUS_ERROR) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
}
|
||||
break;
|
||||
case PROP_CERTIFICATE_SIGNING_REQUEST_FILE:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0],
|
||||
Network_Port_Certificate_Signing_Request_File(
|
||||
rpdata->object_instance));
|
||||
break;
|
||||
/* SC optionals */
|
||||
#if BACNET_SECURE_CONNECT_ROUTING_TABLE
|
||||
case PROP_ROUTING_TABLE:
|
||||
apdu_len = Network_Port_Routing_Table_Encode(
|
||||
rpdata->object_instance, apdu, apdu_size);
|
||||
break;
|
||||
#endif /* BACNET_SECURE_CONNECT_ROUTING_TABLE */
|
||||
#if BSC_CONF_HUB_FUNCTIONS_NUM != 0
|
||||
case PROP_SC_PRIMARY_HUB_CONNECTION_STATUS:
|
||||
apdu_len = bacapp_encode_SCHubConnection(
|
||||
&apdu[0],
|
||||
Network_Port_SC_Primary_Hub_Connection_Status(
|
||||
rpdata->object_instance));
|
||||
break;
|
||||
case PROP_SC_FAILOVER_HUB_CONNECTION_STATUS:
|
||||
apdu_len = bacapp_encode_SCHubConnection(
|
||||
&apdu[0],
|
||||
Network_Port_SC_Failover_Hub_Connection_Status(
|
||||
rpdata->object_instance));
|
||||
break;
|
||||
case PROP_SC_HUB_FUNCTION_ENABLE:
|
||||
apdu_len = encode_application_boolean(
|
||||
&apdu[0],
|
||||
Network_Port_SC_Hub_Function_Enable(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_SC_HUB_FUNCTION_ACCEPT_URIS:
|
||||
apdu_len = bacnet_array_encode(
|
||||
rpdata->object_instance, rpdata->array_index,
|
||||
Network_Port_SC_Hub_Function_Accept_URI_Encode,
|
||||
BACNET_SC_DIRECT_ACCEPT_URI_MAX, apdu, apdu_size);
|
||||
if (apdu_len == BACNET_STATUS_ABORT) {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
} else if (apdu_len == BACNET_STATUS_ERROR) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
}
|
||||
break;
|
||||
case PROP_SC_HUB_FUNCTION_BINDING:
|
||||
Network_Port_SC_Hub_Function_Binding(
|
||||
rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_SC_HUB_FUNCTION_CONNECTION_STATUS:
|
||||
apdu_len = Network_Port_SC_Hub_Function_Connection_Status_Encode(
|
||||
rpdata->object_instance, apdu, apdu_size);
|
||||
break;
|
||||
#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */
|
||||
#if BSC_CONF_HUB_CONNECTORS_NUM != 0
|
||||
case PROP_SC_DIRECT_CONNECT_INITIATE_ENABLE:
|
||||
apdu_len = encode_application_boolean(
|
||||
&apdu[0],
|
||||
Network_Port_SC_Direct_Connect_Initiate_Enable(
|
||||
rpdata->object_instance));
|
||||
break;
|
||||
case PROP_SC_DIRECT_CONNECT_ACCEPT_ENABLE:
|
||||
apdu_len = encode_application_boolean(
|
||||
&apdu[0],
|
||||
Network_Port_SC_Direct_Connect_Accept_Enable(
|
||||
rpdata->object_instance));
|
||||
break;
|
||||
case PROP_SC_DIRECT_CONNECT_ACCEPT_URIS:
|
||||
apdu_len = bacnet_array_encode(
|
||||
rpdata->object_instance, rpdata->array_index,
|
||||
Network_Port_SC_Direct_Connect_Accept_URI_Encode,
|
||||
BACNET_SC_DIRECT_ACCEPT_URI_MAX, apdu, apdu_size);
|
||||
if (apdu_len == BACNET_STATUS_ABORT) {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
} else if (apdu_len == BACNET_STATUS_ERROR) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
}
|
||||
break;
|
||||
case PROP_SC_DIRECT_CONNECT_BINDING:
|
||||
Network_Port_SC_Direct_Connect_Binding(
|
||||
rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_SC_DIRECT_CONNECT_CONNECTION_STATUS:
|
||||
apdu_len = Network_Port_SC_Direct_Connect_Connection_Status_Encode(
|
||||
rpdata->object_instance, apdu, apdu_size);
|
||||
break;
|
||||
#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */
|
||||
case PROP_SC_FAILED_CONNECTION_REQUESTS:
|
||||
apdu_len = Network_Port_SC_Failed_Connection_Requests_Encode(
|
||||
rpdata->object_instance, apdu, apdu_size);
|
||||
break;
|
||||
#endif /* BACDL_BSC */
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
@@ -3693,6 +3953,11 @@ bool Network_Port_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
|
||||
if (!status && (wp_data->error_code == ERROR_CODE_OTHER)) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -3715,7 +3980,7 @@ int Network_Port_Read_Range_BDT(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
* ReadRange service handler for the BACnet/IP FDT.
|
||||
*
|
||||
* @param apdu - place to encode the data
|
||||
* @param apdu - BACNET_READ_RANGE_DATA data
|
||||
* @param pRequest - BACNET_READ_RANGE_DATA data
|
||||
*
|
||||
* @return number of bytes encoded
|
||||
*/
|
||||
@@ -3845,7 +4110,16 @@ void Network_Port_Changes_Discard(void)
|
||||
*/
|
||||
void Network_Port_Cleanup(void)
|
||||
{
|
||||
/* do something interesting */
|
||||
#if defined(BACDL_BSC) && defined(BACNET_SECURE_CONNECT_ROUTING_TABLE)
|
||||
unsigned index = 0;
|
||||
for (index = 0; index < BACNET_NETWORK_PORTS_MAX; index++) {
|
||||
BACNET_SC_PARAMS *sc = &Object_List[index].Network.BSC.Parameters;
|
||||
if (sc->Routing_Table) {
|
||||
Keylist_Delete(sc->Routing_Table);
|
||||
sc->Routing_Table = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3853,5 +4127,58 @@ void Network_Port_Cleanup(void)
|
||||
*/
|
||||
void Network_Port_Init(void)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
#ifdef BACDL_BSC
|
||||
BACNET_SC_PARAMS *sc;
|
||||
#endif /* BACDL_BSC */
|
||||
|
||||
/* do something interesting */
|
||||
|
||||
for (index = 0; index < BACNET_NETWORK_PORTS_MAX; index++) {
|
||||
memset(&Object_List[index], 0, sizeof(Object_List[index]));
|
||||
#ifdef BACDL_BSC
|
||||
Object_List[index].Network_Type = PORT_TYPE_BSC;
|
||||
sc = &Object_List[index].Network.BSC.Parameters;
|
||||
Object_List[index].Activate_Changes =
|
||||
Network_Port_SC_Pending_Params_Apply;
|
||||
Object_List[index].Discard_Changes =
|
||||
Network_Port_SC_Pending_Params_Discard;
|
||||
#ifdef BACNET_SECURE_CONNECT_ROUTING_TABLE
|
||||
sc->Routing_Table = Keylist_Create();
|
||||
#endif
|
||||
sc->SC_Failed_Connection_Requests_Count = 0;
|
||||
#if BSC_CONF_HUB_FUNCTIONS_NUM != 0
|
||||
sc->SC_Hub_Function_Connection_Status_Count = 0;
|
||||
#endif
|
||||
#if BSC_CONF_HUB_CONNECTORS_NUM != 0
|
||||
sc->SC_Direct_Connect_Connection_Status_Count = 0;
|
||||
#endif
|
||||
(void)sc;
|
||||
#endif /* BACDL_BSC */
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BACDL_BSC
|
||||
/**
|
||||
* For a given object instance-number, gets SC parameters structure
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
*
|
||||
* @return SC params structure
|
||||
*/
|
||||
BACNET_SC_PARAMS *Network_Port_SC_Params(uint32_t object_instance)
|
||||
{
|
||||
BACNET_SC_PARAMS *param = NULL;
|
||||
unsigned index = 0;
|
||||
|
||||
index = Network_Port_Instance_To_Index(object_instance);
|
||||
if (index < BACNET_NETWORK_PORTS_MAX) {
|
||||
if (Object_List[index].Network_Type == PORT_TYPE_BSC) {
|
||||
param = &Object_List[index].Network.BSC.Parameters;
|
||||
}
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Helper Network port objects to implement secure connect attributes
|
||||
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
|
||||
* @date December 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_NETPORT_INTERNAL_H
|
||||
#define BACNET_NETPORT_INTERNAL_H
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/basic/sys/keylist.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/datetime.h"
|
||||
#include "bacnet/hostnport.h"
|
||||
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||
#include "bacnet/basic/object/sc_netport.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Support
|
||||
- SC_Hub_Function_Connection_Status
|
||||
- SC_Direct_Connect_Connection_Status
|
||||
- SC_FailedConnectionRequests
|
||||
*/
|
||||
|
||||
typedef struct BACnetSCAttributes_T {
|
||||
BACNET_UNSIGNED_INTEGER Max_BVLC_Length_Accepted;
|
||||
BACNET_UNSIGNED_INTEGER Max_BVLC_Length_Accepted_dirty;
|
||||
BACNET_UNSIGNED_INTEGER Max_NPDU_Length_Accepted;
|
||||
BACNET_UNSIGNED_INTEGER Max_NPDU_Length_Accepted_dirty;
|
||||
char SC_Primary_Hub_URI[BACNET_URI_LENGTH];
|
||||
char SC_Primary_Hub_URI_dirty[BACNET_URI_LENGTH];
|
||||
char SC_Failover_Hub_URI[BACNET_URI_LENGTH];
|
||||
char SC_Failover_Hub_URI_dirty[BACNET_URI_LENGTH];
|
||||
BACNET_UNSIGNED_INTEGER SC_Minimum_Reconnect_Time;
|
||||
BACNET_UNSIGNED_INTEGER SC_Minimum_Reconnect_Time_dirty;
|
||||
BACNET_UNSIGNED_INTEGER SC_Maximum_Reconnect_Time;
|
||||
BACNET_UNSIGNED_INTEGER SC_Maximum_Reconnect_Time_dirty;
|
||||
BACNET_UNSIGNED_INTEGER SC_Connect_Wait_Timeout;
|
||||
BACNET_UNSIGNED_INTEGER SC_Connect_Wait_Timeout_dirty;
|
||||
BACNET_UNSIGNED_INTEGER SC_Disconnect_Wait_Timeout;
|
||||
BACNET_UNSIGNED_INTEGER SC_Disconnect_Wait_Timeout_dirty;
|
||||
BACNET_UNSIGNED_INTEGER SC_Heartbeat_Timeout;
|
||||
BACNET_UNSIGNED_INTEGER SC_Heartbeat_Timeout_dirty;
|
||||
BACNET_SC_HUB_CONNECTOR_STATE SC_Hub_Connector_State;
|
||||
uint32_t Operational_Certificate_File;
|
||||
uint32_t Issuer_Certificate_Files[BACNET_ISSUER_CERT_FILE_MAX];
|
||||
uint32_t Certificate_Signing_Request_File;
|
||||
/* Optional params */
|
||||
#ifdef BACNET_SECURE_CONNECT_ROUTING_TABLE
|
||||
OS_Keylist Routing_Table;
|
||||
#endif /* BACNET_SECURE_CONNECT_ROUTING_TABLE */
|
||||
#if BSC_CONF_HUB_FUNCTIONS_NUM != 0
|
||||
BACNET_SC_HUB_CONNECTION_STATUS SC_Primary_Hub_Connection_Status;
|
||||
BACNET_SC_HUB_CONNECTION_STATUS SC_Failover_Hub_Connection_Status;
|
||||
bool SC_Hub_Function_Enable;
|
||||
bool SC_Hub_Function_Enable_dirty;
|
||||
char SC_Hub_Function_Accept_URIs
|
||||
[BACNET_SC_HUB_URI_MAX * (BACNET_URI_LENGTH + 1)];
|
||||
char SC_Hub_Function_Accept_URIs_dirty
|
||||
[BACNET_SC_HUB_URI_MAX * (BACNET_URI_LENGTH + 1)];
|
||||
char SC_Hub_Function_Binding[BACNET_BINDING_STRING_LENGTH];
|
||||
char SC_Hub_Function_Binding_dirty[BACNET_BINDING_STRING_LENGTH];
|
||||
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS
|
||||
SC_Hub_Function_Connection_Status
|
||||
[BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM];
|
||||
uint8_t SC_Hub_Function_Connection_Status_Count;
|
||||
uint16_t Hub_Server_Port;
|
||||
#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */
|
||||
#if BSC_CONF_HUB_CONNECTORS_NUM != 0
|
||||
bool SC_Direct_Connect_Initiate_Enable;
|
||||
bool SC_Direct_Connect_Initiate_Enable_dirty;
|
||||
bool SC_Direct_Connect_Accept_Enable;
|
||||
bool SC_Direct_Connect_Accept_Enable_dirty;
|
||||
char SC_Direct_Connect_Accept_URIs
|
||||
[BACNET_SC_DIRECT_ACCEPT_URI_MAX * (BACNET_URI_LENGTH + 1)];
|
||||
char SC_Direct_Connect_Accept_URIs_dirty
|
||||
[BACNET_SC_DIRECT_ACCEPT_URI_MAX * (BACNET_URI_LENGTH + 1)];
|
||||
char SC_Direct_Connect_Binding[BACNET_BINDING_STRING_LENGTH];
|
||||
char SC_Direct_Connect_Binding_dirty[BACNET_BINDING_STRING_LENGTH];
|
||||
BACNET_SC_DIRECT_CONNECTION_STATUS SC_Direct_Connect_Connection_Status
|
||||
[BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM];
|
||||
uint8_t SC_Direct_Connect_Connection_Status_Count;
|
||||
uint16_t Direct_Server_Port;
|
||||
#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */
|
||||
BACNET_SC_FAILED_CONNECTION_REQUEST SC_Failed_Connection_Requests
|
||||
[BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM];
|
||||
uint8_t SC_Failed_Connection_Requests_Count;
|
||||
uint32_t Certificate_Key_File;
|
||||
BACNET_UUID Local_UUID;
|
||||
} BACNET_SC_PARAMS;
|
||||
|
||||
BACNET_SC_PARAMS *Network_Port_SC_Params(uint32_t object_instance);
|
||||
void Network_Port_SC_Pending_Params_Apply(uint32_t object_instance);
|
||||
void Network_Port_SC_Pending_Params_Discard(uint32_t object_instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* NETPORT_INTERNAL_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,415 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Helper Network port objects to implement secure connect attributes
|
||||
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
|
||||
* @date June 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_SC_NETPORT_H
|
||||
#define BACNET_SC_NETPORT_H
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/basic/sys/keylist.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/secure_connect.h"
|
||||
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||
|
||||
/* BACnet file instance numbers */
|
||||
#ifndef BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE
|
||||
#define BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE \
|
||||
BSC_CONF_ISSUER_CERTIFICATE_FILE_1_INSTANCE
|
||||
#endif
|
||||
#ifndef BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE
|
||||
#define BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE \
|
||||
BSC_CONF_ISSUER_CERTIFICATE_FILE_2_INSTANCE
|
||||
#endif
|
||||
#ifndef BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE
|
||||
#define BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE \
|
||||
BSC_CONF_OPERATIONAL_CERTIFICATE_FILE_INSTANCE
|
||||
#endif
|
||||
#ifndef BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE
|
||||
#define BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE \
|
||||
BSC_CONF_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define BACNET_SC_HUB_URI_MAX 2
|
||||
#ifndef BACNET_SC_DIRECT_ACCEPT_URI_MAX
|
||||
#define BACNET_SC_DIRECT_ACCEPT_URI_MAX 2
|
||||
#endif
|
||||
|
||||
#define BACNET_ISSUER_CERT_FILE_MAX 2
|
||||
#define BACNET_BINDING_STRING_LENGTH 80
|
||||
|
||||
#define BACNET_SC_BINDING_SEPARATOR ':'
|
||||
|
||||
/* */
|
||||
/* getter / setter */
|
||||
/* */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_UNSIGNED_INTEGER
|
||||
Network_Port_Max_BVLC_Length_Accepted(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_Max_BVLC_Length_Accepted_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
bool Network_Port_Max_BVLC_Length_Accepted_Dirty_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_UNSIGNED_INTEGER
|
||||
Network_Port_Max_NPDU_Length_Accepted(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_Max_NPDU_Length_Accepted_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
bool Network_Port_Max_NPDU_Length_Accepted_Dirty_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Primary_Hub_URI(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *str);
|
||||
const char *Network_Port_SC_Primary_Hub_URI_char(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Primary_Hub_URI_Set(
|
||||
uint32_t object_instance, const char *str);
|
||||
bool Network_Port_SC_Primary_Hub_URI_Dirty_Set(
|
||||
uint32_t object_instance, const char *str);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Failover_Hub_URI(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *str);
|
||||
const char *Network_Port_SC_Failover_Hub_URI_char(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Failover_Hub_URI_Set(
|
||||
uint32_t object_instance, const char *str);
|
||||
bool Network_Port_SC_Failover_Hub_URI_Dirty_Set(
|
||||
uint32_t object_instance, const char *str);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_UNSIGNED_INTEGER
|
||||
Network_Port_SC_Minimum_Reconnect_Time(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Minimum_Reconnect_Time_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
bool Network_Port_SC_Minimum_Reconnect_Time_Dirty_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_UNSIGNED_INTEGER
|
||||
Network_Port_SC_Maximum_Reconnect_Time(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Maximum_Reconnect_Time_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
bool Network_Port_SC_Maximum_Reconnect_Time_Dirty_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_UNSIGNED_INTEGER
|
||||
Network_Port_SC_Connect_Wait_Timeout(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Connect_Wait_Timeout_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
bool Network_Port_SC_Connect_Wait_Timeout_Dirty_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_UNSIGNED_INTEGER
|
||||
Network_Port_SC_Disconnect_Wait_Timeout(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Disconnect_Wait_Timeout_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
bool Network_Port_SC_Disconnect_Wait_Timeout_Dirty_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_UNSIGNED_INTEGER
|
||||
Network_Port_SC_Heartbeat_Timeout(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Heartbeat_Timeout_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
bool Network_Port_SC_Heartbeat_Timeout_Dirty_Set(
|
||||
uint32_t object_instance, BACNET_UNSIGNED_INTEGER value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_HUB_CONNECTOR_STATE
|
||||
Network_Port_SC_Hub_Connector_State(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Hub_Connector_State_Set(
|
||||
uint32_t object_instance, BACNET_SC_HUB_CONNECTOR_STATE value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Network_Port_Operational_Certificate_File(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_Operational_Certificate_File_Set(
|
||||
uint32_t object_instance, uint32_t value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t
|
||||
Network_Port_Issuer_Certificate_File(uint32_t object_instance, uint8_t index);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_Issuer_Certificate_File_Set(
|
||||
uint32_t object_instance, uint8_t index, uint32_t value);
|
||||
BACNET_STACK_EXPORT
|
||||
int Network_Port_Issuer_Certificate_File_Encode(
|
||||
uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t
|
||||
Network_Port_Certificate_Signing_Request_File(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_Certificate_Signing_Request_File_Set(
|
||||
uint32_t object_instance, uint32_t value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_ROUTER_ENTRY *Network_Port_Routing_Table_Find(
|
||||
uint32_t object_instance, uint16_t Network_Number);
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_ROUTER_ENTRY *
|
||||
Network_Port_Routing_Table_Get(uint32_t object_instance, uint8_t index);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_Routing_Table_Add(
|
||||
uint32_t object_instance,
|
||||
uint16_t network_number,
|
||||
uint8_t *mac,
|
||||
uint8_t mac_len,
|
||||
uint8_t status,
|
||||
uint8_t performance_index);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_Routing_Table_Delete(
|
||||
uint32_t object_instance, uint16_t Network_Number);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_Routing_Table_Delete_All(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
uint8_t Network_Port_Routing_Table_Count(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
int Network_Port_Routing_Table_Encode(
|
||||
uint32_t object_instance, uint8_t *apdu, int max_apdu);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_HUB_CONNECTION_STATUS *
|
||||
Network_Port_SC_Primary_Hub_Connection_Status(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Primary_Hub_Connection_Status_Set(
|
||||
uint32_t object_instance,
|
||||
BACNET_SC_CONNECTION_STATE state,
|
||||
BACNET_DATE_TIME *connect_ts,
|
||||
BACNET_DATE_TIME *disconnect_ts,
|
||||
BACNET_ERROR_CODE error,
|
||||
const char *error_details);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_HUB_CONNECTION_STATUS *
|
||||
Network_Port_SC_Failover_Hub_Connection_Status(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Failover_Hub_Connection_Status_Set(
|
||||
uint32_t object_instance,
|
||||
BACNET_SC_CONNECTION_STATE state,
|
||||
BACNET_DATE_TIME *connect_ts,
|
||||
BACNET_DATE_TIME *disconnect_ts,
|
||||
BACNET_ERROR_CODE error,
|
||||
const char *error_details);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Hub_Function_Enable(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Hub_Function_Enable_Set(
|
||||
uint32_t object_instance, bool value);
|
||||
bool Network_Port_SC_Hub_Function_Enable_Dirty_Set(
|
||||
uint32_t object_instance, bool value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Hub_Function_Accept_URI(
|
||||
uint32_t object_instance, uint8_t index, BACNET_CHARACTER_STRING *str);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Hub_Function_Accept_URI_Set(
|
||||
uint32_t object_instance, uint8_t index, const char *str);
|
||||
BACNET_STACK_EXPORT
|
||||
int Network_Port_SC_Hub_Function_Accept_URI_Encode(
|
||||
uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu);
|
||||
|
||||
bool Network_Port_SC_Hub_Function_Accept_URI_Dirty_Set(
|
||||
uint32_t object_instance, uint8_t index, const char *str);
|
||||
const char *
|
||||
Network_Port_SC_Hub_Function_Accept_URIs_char(uint32_t object_instance);
|
||||
bool Network_Port_SC_Hub_Function_Accept_URIs_Set(
|
||||
uint32_t object_instance, const char *str);
|
||||
|
||||
/*
|
||||
Internal representation of the SC_Hub_Function_Binding value is a
|
||||
struct {
|
||||
uint16 port;
|
||||
char interface_name[BACNET_BINDING_STRING_LENGTH - sizeof(uint16)];
|
||||
}
|
||||
External representation is string e.q "interface_name:port_number".
|
||||
|
||||
The Network_Port_SC_Hub_Function_Binding() and
|
||||
the Network_Port_SC_Hub_Function_Binding_Set() do transform from/to.
|
||||
|
||||
The Network_Port_SC_Hub_Function_Binding_Dirty sets value like
|
||||
external representation.
|
||||
*/
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Hub_Function_Binding(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *str);
|
||||
void Network_Port_SC_Hub_Function_Binding_get(
|
||||
uint32_t object_instance, uint16_t *port, char **ifname);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Hub_Function_Binding_Set(
|
||||
uint32_t object_instance, const char *str);
|
||||
bool Network_Port_SC_Hub_Function_Binding_Dirty_Set(
|
||||
uint32_t object_instance, const char *str);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *
|
||||
Network_Port_SC_Hub_Function_Connection_Status_Get(
|
||||
uint32_t object_instance, uint8_t index);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Hub_Function_Connection_Status_Add(
|
||||
uint32_t object_instance,
|
||||
BACNET_SC_CONNECTION_STATE state,
|
||||
BACNET_DATE_TIME *connect_ts,
|
||||
BACNET_DATE_TIME *disconnect_ts,
|
||||
BACNET_HOST_N_PORT_DATA *peer_address,
|
||||
uint8_t *peer_VMAC,
|
||||
uint8_t *peer_UUID,
|
||||
BACNET_ERROR_CODE error,
|
||||
const char *error_details);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Hub_Function_Connection_Status_Delete_All(
|
||||
uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
int Network_Port_SC_Hub_Function_Connection_Status_Count(
|
||||
uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
int Network_Port_SC_Hub_Function_Connection_Status_Encode(
|
||||
uint32_t object_instance, uint8_t *apdu, int max_apdu);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Direct_Connect_Initiate_Enable(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Direct_Connect_Initiate_Enable_Set(
|
||||
uint32_t object_instance, bool value);
|
||||
bool Network_Port_SC_Direct_Connect_Initiate_Enable_Dirty_Set(
|
||||
uint32_t object_instance, bool value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Direct_Connect_Accept_Enable(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Direct_Connect_Accept_Enable_Set(
|
||||
uint32_t object_instance, bool value);
|
||||
bool Network_Port_SC_Direct_Connect_Accept_Enable_Dirty_Set(
|
||||
uint32_t object_instance, bool value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Direct_Connect_Accept_URI(
|
||||
uint32_t object_instance, uint8_t index, BACNET_CHARACTER_STRING *str);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Direct_Connect_Accept_URI_Set(
|
||||
uint32_t object_instance, uint8_t index, const char *str);
|
||||
BACNET_STACK_EXPORT
|
||||
int Network_Port_SC_Direct_Connect_Accept_URI_Encode(
|
||||
uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu);
|
||||
|
||||
bool Network_Port_SC_Direct_Connect_Accept_URI_Dirty_Set(
|
||||
uint32_t object_instance, uint8_t index, const char *str);
|
||||
char *Network_Port_SC_Direct_Connect_Accept_URIs_char(uint32_t object_instance);
|
||||
bool Network_Port_SC_Direct_Connect_Accept_URIs_Set(
|
||||
uint32_t object_instance, const char *str);
|
||||
|
||||
/*
|
||||
Internal representation of the SC_Direct_Connect_Binding value is a
|
||||
struct {
|
||||
uint16 port;
|
||||
char interface_name[BACNET_BINDING_STRING_LENGTH - sizeof(uint16)];
|
||||
}
|
||||
External representation is string e.q "interface_name:port_number".
|
||||
|
||||
The Network_Port_SC_Direct_Connect_Binding() and
|
||||
the Network_Port_SC_Direct_Connect_Binding_Set() do transform from/to.
|
||||
|
||||
The Network_Port_SC_Direct_Connect_Binding_Dirty sets value like
|
||||
external representation.
|
||||
*/
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Direct_Connect_Binding(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *str);
|
||||
void Network_Port_SC_Direct_Connect_Binding_get(
|
||||
uint32_t object_instance, uint16_t *port, char **ifname);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Direct_Connect_Binding_Set(
|
||||
uint32_t object_instance, const char *str);
|
||||
bool Network_Port_SC_Direct_Connect_Binding_Dirty_Set(
|
||||
uint32_t object_instance, const char *str);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_DIRECT_CONNECTION_STATUS *
|
||||
Network_Port_SC_Direct_Connect_Connection_Status_Get(
|
||||
uint32_t object_instance, uint8_t index);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Direct_Connect_Connection_Status_Add(
|
||||
uint32_t object_instance,
|
||||
const char *uri,
|
||||
BACNET_SC_CONNECTION_STATE state,
|
||||
BACNET_DATE_TIME *connect_ts,
|
||||
BACNET_DATE_TIME *disconnect_ts,
|
||||
BACNET_HOST_N_PORT_DATA *peer_address,
|
||||
uint8_t *peer_VMAC,
|
||||
uint8_t *peer_UUID,
|
||||
BACNET_ERROR_CODE error,
|
||||
const char *error_details);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Direct_Connect_Connection_Status_Delete_All(
|
||||
uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
int Network_Port_SC_Direct_Connect_Connection_Status_Count(
|
||||
uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
int Network_Port_SC_Direct_Connect_Connection_Status_Encode(
|
||||
uint32_t object_instance, uint8_t *apdu, int max_apdu);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_FAILED_CONNECTION_REQUEST *
|
||||
Network_Port_SC_Failed_Connection_Requests_Get(
|
||||
uint32_t object_instance, uint8_t index);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Failed_Connection_Requests_Add(
|
||||
uint32_t object_instance,
|
||||
BACNET_DATE_TIME *ts,
|
||||
BACNET_HOST_N_PORT_DATA *peer_address,
|
||||
uint8_t *peer_VMAC,
|
||||
uint8_t *peer_UUID,
|
||||
BACNET_ERROR_CODE error,
|
||||
const char *error_details);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Failed_Connection_Requests_Delete_All(
|
||||
uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
int Network_Port_SC_Failed_Connection_Requests_Count(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
int Network_Port_SC_Failed_Connection_Requests_Encode(
|
||||
uint32_t object_instance, uint8_t *apdu, int max_apdu);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Network_Port_Certificate_Key_File(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_Certificate_Key_File_Set(
|
||||
uint32_t object_instance, uint32_t value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
const BACNET_UUID *Network_Port_SC_Local_UUID(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Network_Port_SC_Local_UUID_Set(
|
||||
uint32_t object_instance, BACNET_UUID *value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -31,6 +31,7 @@
|
||||
#define BACDL_MSTP
|
||||
#define BACDL_BIP
|
||||
#define BACDL_BIP6
|
||||
#define BACDL_BSC
|
||||
#endif
|
||||
|
||||
#if defined(BACDL_ETHERNET)
|
||||
@@ -65,6 +66,13 @@
|
||||
#define BACDL_SOME_DATALINK_ENABLED 1
|
||||
#endif
|
||||
|
||||
#if defined(BACDL_BSC)
|
||||
#if defined(BACDL_SOME_DATALINK_ENABLED)
|
||||
#define BACDL_MULTIPLE 1
|
||||
#endif
|
||||
#define BACDL_SOME_DATALINK_ENABLED 1
|
||||
#endif
|
||||
|
||||
#if defined(BACDL_CUSTOM)
|
||||
#if defined(BACDL_SOME_DATALINK_ENABLED)
|
||||
#define BACDL_MULTIPLE 1
|
||||
@@ -144,6 +152,27 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(BACDL_BSC)
|
||||
#ifndef SC_NETPORT_BVLC_MAX
|
||||
#define SC_NETPORT_BVLC_MAX 1500
|
||||
#endif
|
||||
#ifndef SC_NETPORT_NPDU_MAX
|
||||
#define SC_NETPORT_NPDU_MAX 1500
|
||||
#endif
|
||||
#ifndef SC_NETPORT_CONNECT_TIMEOUT
|
||||
#define SC_NETPORT_CONNECT_TIMEOUT 5
|
||||
#endif
|
||||
#ifndef SC_NETPORT_HEARTBEAT_TIMEOUT
|
||||
#define SC_NETPORT_HEARTBEAT_TIMEOUT 60
|
||||
#endif
|
||||
#ifndef SC_NETPORT_DISCONNECT_TIMEOUT
|
||||
#define SC_NETPORT_DISCONNECT_TIMEOUT 150
|
||||
#endif
|
||||
#ifndef SC_NETPORT_RECONNECT_TIME
|
||||
#define SC_NETPORT_RECONNECT_TIME 2
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* for confirmed messages, this is the number of transactions */
|
||||
/* that we hold in a queue waiting for timeout. */
|
||||
/* Configure to zero if you don't want any confirmed messages */
|
||||
@@ -205,6 +234,7 @@
|
||||
defined(BACAPP_SHED_LEVEL) || \
|
||||
defined(BACAPP_ACCESS_RULE) || \
|
||||
defined(BACAPP_CHANNEL_VALUE) || \
|
||||
defined(BACAPP_SECURE_CONNECT) || \
|
||||
defined(BACAPP_TYPES_EXTRA))
|
||||
#define BACAPP_ALL
|
||||
#endif
|
||||
@@ -253,6 +283,7 @@
|
||||
#define BACAPP_SHED_LEVEL
|
||||
#define BACAPP_ACCESS_RULE
|
||||
#define BACAPP_CHANNEL_VALUE
|
||||
#define BACAPP_SECURE_CONNECT
|
||||
#endif
|
||||
|
||||
/* clang-format off */
|
||||
@@ -270,6 +301,7 @@
|
||||
defined(BACAPP_DEVICE_OBJECT_REFERENCE) || \
|
||||
defined(BACAPP_OBJECT_PROPERTY_REFERENCE) || \
|
||||
defined(BACAPP_DESTINATION) || \
|
||||
defined(BACAPP_SECURE_CONNECT) || \
|
||||
defined(BACAPP_BDT_ENTRY) || \
|
||||
defined(BACAPP_FDT_ENTRY) || \
|
||||
defined(BACAPP_ACTION_COMMAND) || \
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
This readme explains how to enable support of BACNet Secure Connect datalink
|
||||
(ANNEX AB – BACnet Secure Connect in standard), how to set up the building
|
||||
environment and clarifies some important moments regarding implementation
|
||||
of that functionality.
|
||||
|
||||
By default the support of that functionality is turned off, to enable it
|
||||
you need to set option BACDL_BSC=ON in CMakeLists.txt if cmake build system
|
||||
is used or set BACDL_BSC=1 define in a case if using makefiles.
|
||||
|
||||
BACNet/SC standard uses websockets as a transport layer, so Windows/Linux/BSD
|
||||
implementations use libwebsocket library. Implementation for Zephyr uses
|
||||
native websocket API for the client side and mongoose library for the
|
||||
server side. Websocket layer which is built on the top of libwebsockets
|
||||
uses 1 service thread per websocket server instance and 1 service thread
|
||||
per 1 websocket client instance. As a result, libwebsocket must be built
|
||||
with LWS_MAX_SMP > 1, otherwise rarely crashes may ocure in the application
|
||||
which uses bacnet stack library. Recommended value for that define is 32.
|
||||
You should note, that by default libwebsocket is built with LWS_MAX_SMP=1,
|
||||
so a packet manager like vcpkg and apt can have that lib built with
|
||||
LWS_MAX_SMP=1, which can lead to unstable work. It's better to build
|
||||
libwebsockets from recent stable sources for your platform, corresponded
|
||||
examples of how to do that you can find in .github/workflows/bsc-tests-platform.yml,
|
||||
(for example bsc-tests-linux.yml).
|
||||
|
||||
In order to build bacnet stack library for linux, user needs to install
|
||||
libconfig-dev, libcap-dev and libssl-dev to the system. Most easiest way to do
|
||||
that is to use Advanced Packaging Tool (APT), check bsc-test-linux.yml.
|
||||
|
||||
For MacOSX build user must install brew packet manager (https://brew.sh),
|
||||
then install openssl using brew then build libwebsocket, check
|
||||
.github/workflows/bsc-tests-macosx.yml.
|
||||
|
||||
Windows build may be a challenge because libwebsocket depends on libpthreads
|
||||
which not so easy to find and build on windows, refer to the libwebsocket
|
||||
build instruction https://libwebsockets.org/lws-api-doc-master/html/md_README_8build.html.
|
||||
Using of vcpkg packet manager can save a lot of time, but you must ensure that
|
||||
installed libwebsocket library is compiled with LWS_MAX_SMP > 1.
|
||||
A good example how to use vcpkg packet manager with custom build of
|
||||
libwebsocket can be found in .github/workflows/bsc-tests-windows.yml.
|
||||
|
||||
One moment is still needed to be improved. The current implementation processes
|
||||
dataflow between BACNET/SC nodes ignoring max_bvlc_size and max_npdu_size
|
||||
params in connect request and connect accept packets as that is not
|
||||
explicitly explained in standard. As a result if a node which has max_npdu_size
|
||||
less than remote peer can drop received packet if it does not fit into it's internal
|
||||
buffer which depends on BVLC_SC_NPDU_SIZE_CONF parameter. In opposite, a node
|
||||
can send a PDU more than max_npdu_size which may also lead to the drop of PDU
|
||||
on remote peer side.
|
||||
|
||||
The current implementation does not support Certificate_Signing_Request_File
|
||||
property of BACNET/SC netport and properties Operational_Certificate_File
|
||||
and Issuer_Certificate_Files related to certificates are readonly and can't
|
||||
be changed remotely. So, management of device certificates is out of the scope
|
||||
of current BACNet/SC implementation.
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Configuration file of BACNet/SC datalink.
|
||||
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||
* @date August 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_DATALINK_BSC_CONF_H
|
||||
#define BACNET_DATALINK_BSC_CONF_H
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bvlc-sc.h"
|
||||
|
||||
#if !defined(BACDL_BSC)
|
||||
#define BSC_CONF_TX_PRE 0
|
||||
#else
|
||||
#ifndef bsd
|
||||
#define bsd 1
|
||||
#endif
|
||||
#ifndef linux
|
||||
#define linux 2
|
||||
#endif
|
||||
#ifndef win32
|
||||
#define win32 3
|
||||
#endif
|
||||
#if BACNET_PORT == bsd || BACNET_PORT == linux || BACNET_PORT == win32
|
||||
#include <libwebsockets.h>
|
||||
#define BSC_CONF_TX_PRE LWS_PRE
|
||||
#else
|
||||
#define BSC_CONF_TX_PRE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_HUB_CONNECTORS_NUM
|
||||
#define BSC_CONF_HUB_CONNECTORS_NUM 1
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_HUB_FUNCTIONS_NUM
|
||||
#define BSC_CONF_HUB_FUNCTIONS_NUM 1
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_NODE_SWITCHES_NUM
|
||||
#define BSC_CONF_NODE_SWITCHES_NUM 1
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_NODES_NUM
|
||||
#define BSC_CONF_NODES_NUM 1
|
||||
#endif
|
||||
|
||||
#ifndef BVLC_SC_NPDU_SIZE_CONF
|
||||
/* 16 bytes is sum of all sizes of all static (non variable)
|
||||
fields of header of BVLC message */
|
||||
#define BVLC_SC_NPDU_SIZE_CONF ((MAX_PDU) + 16)
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_WEBSOCKET_RX_BUFFER_LEN
|
||||
#define BSC_CONF_WEBSOCKET_RX_BUFFER_LEN BVLC_SC_NPDU_SIZE_CONF
|
||||
#endif
|
||||
|
||||
/* THIS should not be changed, most of BACNet/SC devices must have */
|
||||
/* hub connector, it uses 2 connections */
|
||||
#ifndef BSC_CONF_HUB_CONNECTOR_CONNECTIONS_NUM
|
||||
#define BSC_CONF_HUB_CONNECTOR_CONNECTIONS_NUM (BSC_CONF_HUB_CONNECTORS_NUM * 2)
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM
|
||||
#define BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM (BSC_CONF_HUB_FUNCTIONS_NUM * 10)
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM
|
||||
#define BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM 10
|
||||
#endif
|
||||
|
||||
/* Total amount of client(initiator) webosocket connections */
|
||||
#ifndef BSC_CONF_CLIENT_CONNECTIONS_NUM
|
||||
#define BSC_CONF_CLIENT_CONNECTIONS_NUM \
|
||||
(BSC_CONF_HUB_CONNECTOR_CONNECTIONS_NUM + \
|
||||
BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM * BSC_CONF_NODE_SWITCHES_NUM)
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM
|
||||
#define BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM \
|
||||
(BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM)
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM
|
||||
#define BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM \
|
||||
(BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM * BSC_CONF_NODE_SWITCHES_NUM)
|
||||
#endif
|
||||
|
||||
#define BSC_CONF_SOCKET_TX_BUFFERED_PACKET_NUM 2
|
||||
#define BSC_CONF_DATALINK_BUFFERED_PACKET_NUM 10
|
||||
|
||||
#define BSC_CONF_SOCK_RX_BUFFER_SIZE BVLC_SC_NPDU_SIZE_CONF
|
||||
|
||||
/* 2 bytes is a prefix containing BVLC message length.
|
||||
BSC_CONF_TX_PRE - some reserved bytes before actual payload.
|
||||
Some libs like libwebsocket requires some bytes to be reserved
|
||||
before actual payload for sending, so BSC_CONF_TX_PRE is used for
|
||||
that purpose (it allows to avoid copying of payload and
|
||||
buffer reallocation)
|
||||
*/
|
||||
|
||||
#define BSC_CONF_SOCK_TX_BUFFER_SIZE \
|
||||
((BVLC_SC_NPDU_SIZE_CONF + 2 + BSC_CONF_TX_PRE) * \
|
||||
BSC_CONF_SOCKET_TX_BUFFERED_PACKET_NUM)
|
||||
|
||||
/* datalink RX buffer size is always rounded to next power of two */
|
||||
/* so if final buffer size is 1628 it will be rounded to 2048 */
|
||||
|
||||
#define BSC_CONF_DATALINK_RX_BUFFER_SIZE \
|
||||
(BVLC_SC_NPDU_SIZE_CONF * BSC_CONF_DATALINK_BUFFERED_PACKET_NUM)
|
||||
|
||||
#ifndef BSC_CONF_WSURL_MAX_LEN
|
||||
#define BSC_CONF_WSURL_MAX_LEN 128
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN
|
||||
#define BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN 128
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK
|
||||
#define BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK \
|
||||
BSC_CONF_WSURL_MAX_LEN
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_NODE_MAX_URIS_NUM_IN_ADDRESS_RESOLUTION_ACK
|
||||
#define BSC_CONF_NODE_MAX_URIS_NUM_IN_ADDRESS_RESOLUTION_ACK \
|
||||
(BSC_CONF_SOCK_RX_BUFFER_SIZE / \
|
||||
BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK - \
|
||||
1)
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_OPERATIONAL_CERTIFICATE_FILE_INSTANCE
|
||||
#define BSC_CONF_OPERATIONAL_CERTIFICATE_FILE_INSTANCE 5
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE
|
||||
#define BSC_CONF_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE 6
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_ISSUER_CERTIFICATE_FILE_1_INSTANCE
|
||||
#define BSC_CONF_ISSUER_CERTIFICATE_FILE_1_INSTANCE 7
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_ISSUER_CERTIFICATE_FILE_2_INSTANCE
|
||||
#define BSC_CONF_ISSUER_CERTIFICATE_FILE_2_INSTANCE 8
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_ISSUER_CERTIFICATE_FILE_3_INSTANCE
|
||||
#define BSC_CONF_ISSUER_CERTIFICATE_FILE_3_INSTANCE 9
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM
|
||||
#define BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM \
|
||||
BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM
|
||||
#define BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM \
|
||||
BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM
|
||||
#define BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM 4
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,653 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief BACnet secure connect hub function API.
|
||||
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||
* @date October 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include <bacnet/basic/sys/fifo.h>
|
||||
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||
#include "bacnet/datalink/bsc/bsc-datalink.h"
|
||||
#include "bacnet/datalink/bsc/bsc-socket.h"
|
||||
#include "bacnet/datalink/bsc/bsc-util.h"
|
||||
#include "bacnet/datalink/bsc/bsc-event.h"
|
||||
#include "bacnet/datalink/bsc/bsc-hub-function.h"
|
||||
#include "bacnet/datalink/bsc/bsc-node.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/basic/object/netport.h"
|
||||
#include "bacnet/basic/object/sc_netport.h"
|
||||
#include "bacnet/basic/object/bacfile.h"
|
||||
|
||||
#define PRINTF debug_perror
|
||||
#define DEBUG_BSC_DATALINK 0
|
||||
#if DEBUG_BSC_DATALINK == 1
|
||||
#define DEBUG_PRINTF debug_printf
|
||||
#else
|
||||
#define DEBUG_PRINTF debug_printf_disabled
|
||||
#endif
|
||||
|
||||
#define BSC_NEXT_POWER_OF_TWO1(v) \
|
||||
((((unsigned int)v) - 1) | ((((unsigned int)v) - 1) >> 1))
|
||||
#define BSC_NEXT_POWER_OF_TWO2(v) \
|
||||
(BSC_NEXT_POWER_OF_TWO1(v) | BSC_NEXT_POWER_OF_TWO1(v) >> 2)
|
||||
#define BSC_NEXT_POWER_OF_TWO3(v) \
|
||||
(BSC_NEXT_POWER_OF_TWO2(v) | BSC_NEXT_POWER_OF_TWO2(v) >> 4)
|
||||
#define BSC_NEXT_POWER_OF_TWO4(v) \
|
||||
(BSC_NEXT_POWER_OF_TWO3(v) | BSC_NEXT_POWER_OF_TWO3(v) >> 8)
|
||||
#define BSC_NEXT_POWER_OF_TWO(v) \
|
||||
((BSC_NEXT_POWER_OF_TWO4(v) | BSC_NEXT_POWER_OF_TWO4(v) >> 16)) + 1
|
||||
|
||||
typedef enum {
|
||||
BSC_DATALINK_STATE_IDLE = 0,
|
||||
BSC_DATALINK_STATE_STARTING = 1,
|
||||
BSC_DATALINK_STATE_STARTED = 2,
|
||||
BSC_DATALINK_STATE_STOPPING = 3
|
||||
} BSC_DATALINK_STATE;
|
||||
|
||||
static FIFO_BUFFER bsc_fifo = { 0 };
|
||||
static uint8_t
|
||||
bsc_fifo_buf[BSC_NEXT_POWER_OF_TWO(BSC_CONF_DATALINK_RX_BUFFER_SIZE)];
|
||||
static BSC_NODE *bsc_node = NULL;
|
||||
static BSC_NODE_CONF bsc_conf;
|
||||
static BSC_DATALINK_STATE bsc_datalink_state = BSC_DATALINK_STATE_IDLE;
|
||||
static BSC_EVENT *bsc_event = NULL;
|
||||
static BSC_EVENT *bsc_data_event = NULL;
|
||||
|
||||
/**
|
||||
* @brief bsc_node_event() is a callback function which is called by
|
||||
* BACnet/SC datalink when some event occurs.
|
||||
* The function is used to notify the upper layer about the events.
|
||||
* @param node - pointer to the BACnet/SC node
|
||||
* @param ev - event type
|
||||
* @param dest - pointer to the destination address
|
||||
* @param pdu - pointer to the received PDU
|
||||
* @param pdu_len - length of the received PDU
|
||||
*/
|
||||
static void bsc_node_event(
|
||||
BSC_NODE *node,
|
||||
BSC_NODE_EVENT ev,
|
||||
BACNET_SC_VMAC_ADDRESS *dest,
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len)
|
||||
{
|
||||
uint16_t pdu16_len;
|
||||
DEBUG_PRINTF("bsc_node_event() >>> ev = %d\n", ev);
|
||||
bws_dispatch_lock();
|
||||
(void)node;
|
||||
(void)dest;
|
||||
if (ev == BSC_NODE_EVENT_STARTED || ev == BSC_NODE_EVENT_STOPPED) {
|
||||
if (bsc_datalink_state != BSC_DATALINK_STATE_IDLE) {
|
||||
bsc_event_signal(bsc_event);
|
||||
}
|
||||
} else if (ev == BSC_NODE_EVENT_RECEIVED_NPDU) {
|
||||
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||
if (pdu_len <= USHRT_MAX &&
|
||||
FIFO_Available(
|
||||
&bsc_fifo, (unsigned)(pdu_len + sizeof(pdu16_len)))) {
|
||||
pdu16_len = (uint16_t)pdu_len;
|
||||
FIFO_Add(&bsc_fifo, (uint8_t *)&pdu16_len, sizeof(pdu16_len));
|
||||
FIFO_Add(&bsc_fifo, pdu, pdu16_len);
|
||||
bsc_event_signal(bsc_data_event);
|
||||
}
|
||||
#if DEBUG_ENABLED == 1
|
||||
else {
|
||||
PRINTF("pdu of size %d\n is dropped\n", pdu_len);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_node_event() <<<\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief bsc_deinit_resources() is a function which is used to
|
||||
* deinitialize all resources allocated by BACnet/SC datalink.
|
||||
*/
|
||||
static void bsc_deinit_resources(void)
|
||||
{
|
||||
if (bsc_event) {
|
||||
bsc_event_deinit(bsc_event);
|
||||
bsc_event = NULL;
|
||||
}
|
||||
if (bsc_data_event) {
|
||||
bsc_event_deinit(bsc_data_event);
|
||||
bsc_data_event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the BACnet/SC datalink layer
|
||||
* @param ifname - name of the network interface
|
||||
* @return true if the initialization was successful, otherwise false
|
||||
*/
|
||||
bool bsc_init(char *ifname)
|
||||
{
|
||||
BSC_SC_RET r;
|
||||
bool ret = false;
|
||||
DEBUG_PRINTF("bsc_init() >>>\n");
|
||||
|
||||
(void)ifname;
|
||||
|
||||
bws_dispatch_lock();
|
||||
|
||||
if (bsc_datalink_state != BSC_DATALINK_STATE_IDLE) {
|
||||
bws_dispatch_unlock();
|
||||
PRINTF("bsc_init() <<< ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bsc_event = bsc_event_init();
|
||||
bsc_data_event = bsc_event_init();
|
||||
|
||||
if (!bsc_event || !bsc_data_event) {
|
||||
bsc_deinit_resources();
|
||||
bws_dispatch_unlock();
|
||||
PRINTF("bsc_init() <<< ret = %d\n", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_PRINTF(
|
||||
"bsc_init() BACNET/SC datalink configured to use input fifo "
|
||||
"of size %d\n",
|
||||
sizeof(bsc_fifo_buf));
|
||||
FIFO_Init(&bsc_fifo, bsc_fifo_buf, sizeof(bsc_fifo_buf));
|
||||
|
||||
ret = bsc_node_conf_fill_from_netport(&bsc_conf, &bsc_node_event);
|
||||
|
||||
if (!ret) {
|
||||
bsc_deinit_resources();
|
||||
bws_dispatch_unlock();
|
||||
PRINTF("bsc_init() <<< configuration of BACNET/SC datalink "
|
||||
"failed, ret = false\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
bsc_datalink_state = BSC_DATALINK_STATE_STARTING;
|
||||
r = bsc_node_init(&bsc_conf, &bsc_node);
|
||||
if (r == BSC_SC_SUCCESS) {
|
||||
r = bsc_node_start(bsc_node);
|
||||
if (r == BSC_SC_SUCCESS) {
|
||||
bws_dispatch_unlock();
|
||||
bsc_event_wait(bsc_event);
|
||||
bws_dispatch_lock();
|
||||
bsc_datalink_state = BSC_DATALINK_STATE_STARTED;
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_init() <<< ret = %d\n", true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
bsc_deinit_resources();
|
||||
bsc_node_conf_cleanup(&bsc_conf);
|
||||
bsc_datalink_state = BSC_DATALINK_STATE_IDLE;
|
||||
bws_dispatch_unlock();
|
||||
PRINTF("bsc_init() <<< ret = %d\n", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Blocking thread-safe bsc_cleanup() function
|
||||
* de-initializes BACNet/SC datalink.
|
||||
*/
|
||||
void bsc_cleanup(void)
|
||||
{
|
||||
DEBUG_PRINTF("bsc_cleanup() >>>\n");
|
||||
bws_dispatch_lock();
|
||||
if (bsc_datalink_state != BSC_DATALINK_STATE_IDLE &&
|
||||
bsc_datalink_state != BSC_DATALINK_STATE_STOPPING) {
|
||||
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTING) {
|
||||
bws_dispatch_unlock();
|
||||
bsc_event_wait(bsc_event);
|
||||
bws_dispatch_lock();
|
||||
}
|
||||
if (bsc_datalink_state != BSC_DATALINK_STATE_STOPPING) {
|
||||
bsc_datalink_state = BSC_DATALINK_STATE_STOPPING;
|
||||
bsc_event_signal(bsc_data_event);
|
||||
bsc_node_stop(bsc_node);
|
||||
bws_dispatch_unlock();
|
||||
bsc_event_wait(bsc_event);
|
||||
bsc_event_wait(bsc_data_event);
|
||||
bws_dispatch_lock();
|
||||
bsc_deinit_resources();
|
||||
(void)bsc_node_deinit(bsc_node);
|
||||
bsc_node_conf_cleanup(&bsc_conf);
|
||||
bsc_node = NULL;
|
||||
bsc_datalink_state = BSC_DATALINK_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_cleanup() <<<\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send a BACnet/SC PDU to a remote node
|
||||
* @param dest - destination address
|
||||
* @param npdu_data - network layer data
|
||||
* @param pdu - PDU to send
|
||||
* @param pdu_len - length of the PDU
|
||||
* @return number of bytes sent on success, negative number on failure
|
||||
*/
|
||||
int bsc_send_pdu(
|
||||
BACNET_ADDRESS *dest,
|
||||
BACNET_NPDU_DATA *npdu_data,
|
||||
uint8_t *pdu,
|
||||
unsigned pdu_len)
|
||||
{
|
||||
BSC_SC_RET ret;
|
||||
BACNET_SC_VMAC_ADDRESS dest_vmac;
|
||||
int len = -1;
|
||||
static uint8_t buf[BVLC_SC_NPDU_SIZE_CONF];
|
||||
|
||||
/* this datalink doesn't need to know the npdu data */
|
||||
(void)npdu_data;
|
||||
|
||||
bws_dispatch_lock();
|
||||
|
||||
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||
if (dest->net == BACNET_BROADCAST_NETWORK || dest->mac_len == 0) {
|
||||
/* broadcast message */
|
||||
memset(&dest_vmac.address[0], 0xFF, BVLC_SC_VMAC_SIZE);
|
||||
} else if (dest->mac_len == BVLC_SC_VMAC_SIZE) {
|
||||
/* unicast */
|
||||
memcpy(&dest_vmac.address[0], &dest->mac[0], BVLC_SC_VMAC_SIZE);
|
||||
} else {
|
||||
bws_dispatch_unlock();
|
||||
PRINTF("bsc_send_pdu() <<< ret = -1, incorrect dest mac address\n");
|
||||
return len;
|
||||
}
|
||||
|
||||
len = (int)bvlc_sc_encode_encapsulated_npdu(
|
||||
buf, sizeof(buf), bsc_get_next_message_id(), NULL, &dest_vmac, pdu,
|
||||
pdu_len);
|
||||
|
||||
ret = bsc_node_send(bsc_node, buf, len);
|
||||
len = pdu_len;
|
||||
|
||||
if (ret != BSC_SC_SUCCESS) {
|
||||
len = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_send_pdu() <<< ret = %d\n", len);
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove a BACnet/SC packet from the FIFO buffer
|
||||
* @param packet_size - size of the packet to remove
|
||||
*/
|
||||
static void bsc_remove_packet(size_t packet_size)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < packet_size; i++) {
|
||||
(void)FIFO_Get(&bsc_fifo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Blocking thread-safe bsc_receive() function
|
||||
* receives NPDUs transferred over BACNet/SC
|
||||
* from a node specified by it's virtual MAC address as
|
||||
* defined in Clause AB.1.5.2.
|
||||
* @param src - source VMAC address
|
||||
* @param pdu - a buffer to hold the PDU portion of the received packet,
|
||||
* after the BVLC portion has been stripped off.
|
||||
* @param max_pdu - size of the pdu[] buffer
|
||||
* @param timeout_ms - the number of milliseconds to wait for a packet
|
||||
* @return the number of octets (remaining) in the PDU, or zero if no packet
|
||||
*/
|
||||
uint16_t bsc_receive(
|
||||
BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu, unsigned timeout_ms)
|
||||
{
|
||||
uint16_t pdu_len = 0;
|
||||
uint16_t npdu16_len = 0;
|
||||
BVLC_SC_DECODED_MESSAGE dm;
|
||||
BACNET_ERROR_CODE error;
|
||||
BACNET_ERROR_CLASS class;
|
||||
const char *err_desc = NULL;
|
||||
static uint8_t buf[BVLC_SC_NPDU_SIZE_CONF];
|
||||
|
||||
DEBUG_PRINTF("bsc_receive() >>>\n");
|
||||
|
||||
bws_dispatch_lock();
|
||||
|
||||
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||
if (FIFO_Count(&bsc_fifo) <= sizeof(npdu16_len)) {
|
||||
bws_dispatch_unlock();
|
||||
bsc_event_timedwait(bsc_data_event, timeout_ms);
|
||||
bws_dispatch_lock();
|
||||
}
|
||||
|
||||
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED &&
|
||||
FIFO_Count(&bsc_fifo) > sizeof(npdu16_len)) {
|
||||
DEBUG_PRINTF("bsc_receive() processing data...\n");
|
||||
FIFO_Pull(&bsc_fifo, (uint8_t *)&npdu16_len, sizeof(npdu16_len));
|
||||
|
||||
if (sizeof(buf) < npdu16_len) {
|
||||
PRINTF("bsc_receive() pdu of size %d is dropped\n", npdu16_len);
|
||||
bsc_remove_packet(npdu16_len);
|
||||
} else {
|
||||
FIFO_Pull(&bsc_fifo, buf, npdu16_len);
|
||||
if (!bvlc_sc_decode_message(
|
||||
buf, npdu16_len, &dm, &error, &class, &err_desc)) {
|
||||
PRINTF(
|
||||
"bsc_receive() pdu of size %d is dropped because "
|
||||
"of err = %d, class %d, desc = %s\n",
|
||||
npdu16_len, error, class, err_desc);
|
||||
bsc_remove_packet(npdu16_len);
|
||||
} else {
|
||||
if (dm.hdr.origin &&
|
||||
max_pdu >= dm.payload.encapsulated_npdu.npdu_len) {
|
||||
src->mac_len = BVLC_SC_VMAC_SIZE;
|
||||
memcpy(
|
||||
&src->mac[0], &dm.hdr.origin->address[0],
|
||||
BVLC_SC_VMAC_SIZE);
|
||||
memcpy(
|
||||
pdu, dm.payload.encapsulated_npdu.npdu,
|
||||
dm.payload.encapsulated_npdu.npdu_len);
|
||||
pdu_len =
|
||||
(uint16_t)dm.payload.encapsulated_npdu.npdu_len;
|
||||
}
|
||||
#if DEBUG_ENABLED == 1
|
||||
else {
|
||||
PRINTF(
|
||||
"bsc_receive() pdu of size %d is dropped "
|
||||
"because origin addr is absent or output "
|
||||
"buf of size %d is to small\n",
|
||||
npdu16_len, max_pdu);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTF("bsc_receive() pdu_len = %d\n", pdu_len);
|
||||
}
|
||||
}
|
||||
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_receive() <<< ret = %d\n", pdu_len);
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function can be used to retrieve broadcast
|
||||
* VMAC address for BACNet/SC node.
|
||||
* @param dest - value of broadcast VMAC address
|
||||
*/
|
||||
void bsc_get_broadcast_address(BACNET_ADDRESS *dest)
|
||||
{
|
||||
if (dest) {
|
||||
dest->net = BACNET_BROADCAST_NETWORK;
|
||||
dest->mac_len = BVLC_SC_VMAC_SIZE;
|
||||
memset(&dest->mac[0], 0xFF, BVLC_SC_VMAC_SIZE);
|
||||
/* no SADR */
|
||||
dest->len = 0;
|
||||
memset(dest->adr, 0, sizeof(dest->adr));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function can be used to retrieve local
|
||||
* VMAC address of initialized BACNet/SC datalink.
|
||||
* @param my_address - value of local VMAC address
|
||||
*/
|
||||
void bsc_get_my_address(BACNET_ADDRESS *my_address)
|
||||
{
|
||||
if (my_address) {
|
||||
memset(my_address, 0, sizeof(*my_address));
|
||||
}
|
||||
|
||||
bws_dispatch_lock();
|
||||
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||
my_address->mac_len = BVLC_SC_VMAC_SIZE;
|
||||
memcpy(
|
||||
&my_address->mac[0], &bsc_conf.local_vmac.address[0],
|
||||
BVLC_SC_VMAC_SIZE);
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine if the BACnet/SC direct connection is established
|
||||
* with a remote BACnet/SC node.
|
||||
* @param dest - BACnet/SC VMAC address of the remote node to check direct
|
||||
* connection status
|
||||
* @param urls - array representing the possible URIs of a remote node for
|
||||
* acceptance of direct connections. Can contain 1 element
|
||||
* @param urls_cnt - number of elements in the urls array
|
||||
* @return true if the connection is established, otherwise false
|
||||
*/
|
||||
bool bsc_direct_connection_established(
|
||||
BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt)
|
||||
{
|
||||
bool ret = false;
|
||||
bws_dispatch_lock();
|
||||
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||
ret = bsc_node_direct_connection_established(
|
||||
bsc_node, dest, urls, urls_cnt);
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start the process of establishing a direct BACnet/SC connection
|
||||
* to a node identified by either urls or dest parameter. The user should
|
||||
* note that if the dest parameter is used, the local node tries to resolve
|
||||
* it (e.g. to get URIs related to dest VMAC from all existing BACnet/SC
|
||||
* nodes in the network). As a result, the process of establishing a BACnet/SC
|
||||
* connection by dest may take an unpredictable amount of time depending on
|
||||
* the current network configuration.
|
||||
* @param dest - BACnet/SC VMAC address of the remote node to check direct
|
||||
* connection status
|
||||
* @param urls - array representing the possible URIs of a remote node for
|
||||
* acceptance of direct connections. Can contain 1 element
|
||||
* @param urls_cnt - number of elements in the urls array
|
||||
* @return BSC_SC_SUCCESS if the process of establishing a BACnet/SC
|
||||
* connection was started successfully, otherwise an error code
|
||||
*/
|
||||
BSC_SC_RET
|
||||
bsc_connect_direct(BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt)
|
||||
{
|
||||
BSC_SC_RET ret = BSC_SC_INVALID_OPERATION;
|
||||
DEBUG_PRINTF(
|
||||
"bsc_connect_direct() >>> dest = %p, urls = %p, urls_cnt = %d\n", dest,
|
||||
urls, urls_cnt);
|
||||
bws_dispatch_lock();
|
||||
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||
ret = bsc_node_connect_direct(bsc_node, dest, urls, urls_cnt);
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_connect_direct() ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnect a direct BACnet/SC connection with a remote node
|
||||
* identified by its VMAC address.
|
||||
* @param dest - BACnet/SC VMAC address of the remote node to disconnect
|
||||
* the direct connection with
|
||||
*/
|
||||
void bsc_disconnect_direct(BACNET_SC_VMAC_ADDRESS *dest)
|
||||
{
|
||||
bws_dispatch_lock();
|
||||
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||
bsc_node_disconnect_direct(bsc_node, dest);
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process the hub connector state
|
||||
*/
|
||||
static void bsc_update_hub_connector_state(void)
|
||||
{
|
||||
BACNET_SC_HUB_CONNECTOR_STATE state;
|
||||
uint32_t instance;
|
||||
|
||||
instance = Network_Port_Index_To_Instance(0);
|
||||
state = bsc_node_hub_connector_state(bsc_node);
|
||||
Network_Port_SC_Hub_Connector_State_Set(instance, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process the hub connector status
|
||||
*/
|
||||
static void bsc_update_hub_connector_status(void)
|
||||
{
|
||||
BACNET_SC_HUB_CONNECTION_STATUS *status;
|
||||
uint32_t instance;
|
||||
|
||||
instance = Network_Port_Index_To_Instance(0);
|
||||
status = bsc_node_hub_connector_status(bsc_node, true);
|
||||
if (status) {
|
||||
Network_Port_SC_Primary_Hub_Connection_Status_Set(
|
||||
instance, status->State, &status->Connect_Timestamp,
|
||||
&status->Disconnect_Timestamp, status->Error,
|
||||
status->Error_Details[0] ? status->Error_Details : NULL);
|
||||
}
|
||||
status = bsc_node_hub_connector_status(bsc_node, false);
|
||||
if (status) {
|
||||
Network_Port_SC_Failover_Hub_Connection_Status_Set(
|
||||
instance, status->State, &status->Connect_Timestamp,
|
||||
&status->Disconnect_Timestamp, status->Error,
|
||||
status->Error_Details[0] ? status->Error_Details : NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process the hub function status
|
||||
*/
|
||||
static void bsc_update_hub_function_status(void)
|
||||
{
|
||||
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *s;
|
||||
size_t cnt;
|
||||
int i;
|
||||
uint32_t instance = Network_Port_Index_To_Instance(0);
|
||||
BACNET_SC_VMAC_ADDRESS uninitialized = { 0 };
|
||||
|
||||
s = bsc_node_hub_function_status(bsc_node, &cnt);
|
||||
if (s) {
|
||||
Network_Port_SC_Hub_Function_Connection_Status_Delete_All(instance);
|
||||
for (i = 0; i < cnt; i++) {
|
||||
if (memcmp(
|
||||
&uninitialized.address[0], &s[i].Peer_VMAC[0],
|
||||
BVLC_SC_VMAC_SIZE) != 0) {
|
||||
Network_Port_SC_Hub_Function_Connection_Status_Add(
|
||||
instance, s[i].State, &s[i].Connect_Timestamp,
|
||||
&s[i].Disconnect_Timestamp, &s[i].Peer_Address,
|
||||
s[i].Peer_VMAC, s[i].Peer_UUID.uuid.uuid128, s[i].Error,
|
||||
s[i].Error_Details[0] == 0 ? NULL : s[i].Error_Details);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add direct connection status to the network port
|
||||
* @param s - direct connection status
|
||||
* @param cnt - number of direct connection statuses
|
||||
*/
|
||||
static void bsc_add_direct_status_to_netport(
|
||||
BACNET_SC_DIRECT_CONNECTION_STATUS *s, size_t cnt)
|
||||
{
|
||||
int i;
|
||||
uint32_t instance = Network_Port_Index_To_Instance(0);
|
||||
BACNET_SC_VMAC_ADDRESS uninitialized = { 0 };
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
if (memcmp(
|
||||
&uninitialized.address[0], &s[i].Peer_VMAC[0],
|
||||
BVLC_SC_VMAC_SIZE) != 0) {
|
||||
Network_Port_SC_Direct_Connect_Connection_Status_Add(
|
||||
instance, s[i].URI[0] == 0 ? NULL : s[i].URI, s[i].State,
|
||||
&s[i].Connect_Timestamp, &s[i].Disconnect_Timestamp,
|
||||
&s[i].Peer_Address, s[i].Peer_VMAC, s[i].Peer_UUID.uuid.uuid128,
|
||||
s[i].Error,
|
||||
s[i].Error_Details[0] == 0 ? NULL : s[i].Error_Details);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process the direct connection status
|
||||
*/
|
||||
static void bsc_update_direct_connection_status(void)
|
||||
{
|
||||
BACNET_SC_DIRECT_CONNECTION_STATUS *s = NULL;
|
||||
size_t cnt = 0;
|
||||
uint32_t instance = Network_Port_Index_To_Instance(0);
|
||||
|
||||
s = bsc_node_direct_connection_status(bsc_node, &cnt);
|
||||
if (s) {
|
||||
Network_Port_SC_Direct_Connect_Connection_Status_Delete_All(instance);
|
||||
if (s && cnt) {
|
||||
bsc_add_direct_status_to_netport(s, cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process the failed requests
|
||||
*/
|
||||
static void bsc_update_failed_requests(void)
|
||||
{
|
||||
BACNET_SC_FAILED_CONNECTION_REQUEST *r;
|
||||
size_t cnt;
|
||||
int i;
|
||||
|
||||
uint32_t instance = Network_Port_Index_To_Instance(0);
|
||||
r = bsc_node_failed_requests_status(bsc_node, &cnt);
|
||||
if (r) {
|
||||
Network_Port_SC_Failed_Connection_Requests_Delete_All(instance);
|
||||
for (i = 0; i < cnt; i++) {
|
||||
if (r[i].Peer_Address.host[0] != 0) {
|
||||
#if DEBUG_ENABLED == 1
|
||||
DEBUG_PRINTF(
|
||||
"failed request record %d, host %s, vmac %s, uuid "
|
||||
"%s, error %d, details = %s\n",
|
||||
i, r[i].Peer_Address.host,
|
||||
bsc_vmac_to_string(
|
||||
(BACNET_SC_VMAC_ADDRESS *)r[i].Peer_VMAC),
|
||||
bsc_uuid_to_string(
|
||||
(BACNET_SC_UUID *)r[i].Peer_UUID.uuid.uuid128),
|
||||
r[i].Error, r[i].Error_Details);
|
||||
#endif
|
||||
Network_Port_SC_Failed_Connection_Requests_Add(
|
||||
instance, &r[i].Timestamp, &r[i].Peer_Address,
|
||||
r[i].Peer_VMAC, r[i].Peer_UUID.uuid.uuid128, r[i].Error,
|
||||
r[i].Error_Details[0] != 0 ? r[i].Error_Details : NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the network port properties
|
||||
*/
|
||||
static void bsc_update_netport_properties(void)
|
||||
{
|
||||
if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) {
|
||||
bsc_update_hub_connector_state();
|
||||
bsc_update_hub_connector_status();
|
||||
bsc_update_hub_function_status();
|
||||
bsc_update_direct_connection_status();
|
||||
bsc_update_failed_requests();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Manage the BACnet/SC datalink timer
|
||||
* @param seconds - number of elapsed seconds
|
||||
*/
|
||||
void bsc_maintenance_timer(uint16_t seconds)
|
||||
{
|
||||
bws_dispatch_lock();
|
||||
bsc_node_maintenance_timer(seconds);
|
||||
bsc_update_netport_properties();
|
||||
bws_dispatch_unlock();
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief BACNet/SC datalink public interface API.
|
||||
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||
* @date October 2022
|
||||
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*/
|
||||
#ifndef BACNET_DATALINK_BSC_DATALINK_H
|
||||
#define BACNET_DATALINK_BSC_DATALINK_H
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||
|
||||
/**
|
||||
* @brief Blocking thread-safe bsc_init() function
|
||||
* initializes BACNet/SC datalink in accordence with value of properties
|
||||
* from BACNet/SC Network Port Object (sc_netport.h). That means that
|
||||
* user must initialize and set corresponded properties required for
|
||||
* configuration of BACNet/SC datalink before calling of that function.
|
||||
* According to "Addendum cc" to ANSI/ASHRAE Standard 135-2020
|
||||
* https://bacnet.org/wp-content/uploads/sites/4/2022/08/Add-135-2020cc.pdf
|
||||
* most important properties are:
|
||||
* SC_Primary_Hub_URI
|
||||
* SC_Failover_Hub_URI
|
||||
* SC_Maximum_Reconnect_Time
|
||||
* SC_Connect_Wait_Timeout
|
||||
* SC_Disconnect_Wait_Timeout
|
||||
* SC_Heartbeat_Timeout
|
||||
* Operational_Certificate_File
|
||||
* Issuer_Certificate_Files
|
||||
* SC_Hub_Function_Enable
|
||||
* SC_Hub_Function_Binding
|
||||
* SC_Direct_Connect_Initiate_Enable
|
||||
* SC_Direct_Connect_Binding
|
||||
* SC_Direct_Connect_Accept_Enable
|
||||
* @param ifname - ignored and unused it was added for backward compatibility.
|
||||
* @return true if datalink was initiated and started, otherwise returns false.
|
||||
*/
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bsc_init(char *ifname);
|
||||
|
||||
/**
|
||||
* @brief Blocking thread-safe bsc_cleanup() function
|
||||
* de-initializes BACNet/SC datalink.
|
||||
*/
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_cleanup(void);
|
||||
/**
|
||||
* @brief Function checks if all needed certificate file are present.
|
||||
* @return true if all needed certificate file are present otherwise returns
|
||||
* false.
|
||||
*/
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bsc_cert_files_check(void);
|
||||
|
||||
/**
|
||||
* @brief Blocking thread-safe bsc_send_pdu() function
|
||||
* sends pdu over BACNet/SC to node specified by
|
||||
* destination address param.
|
||||
*
|
||||
* @param dest [in] BACNet/SC node's virtual MAC address as
|
||||
* defined in Clause AB.1.5.2.
|
||||
* Can be broadcast.
|
||||
* @param npdu_data [in] BACNet/SC datalink does not use that
|
||||
* parameter. Added for backward
|
||||
* compatibility.
|
||||
* @param pdu [in] protocol data unit to be sent.
|
||||
* @param pdu_len [in] - number of bytes to send.
|
||||
* @return Number of bytes sent on success, negative number on failure.
|
||||
*/
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bsc_send_pdu(
|
||||
BACNET_ADDRESS *dest,
|
||||
BACNET_NPDU_DATA *npdu_data,
|
||||
uint8_t *pdu,
|
||||
unsigned pdu_len);
|
||||
|
||||
/**
|
||||
* @brief Blocking thread-safe bsc_receive() function
|
||||
* receives NPDUs transferred over BACNet/SC
|
||||
* from a node specified by it's virtual MAC address as
|
||||
* defined in Clause AB.1.5.2.
|
||||
*
|
||||
* @param src [out] Source VMAC address
|
||||
* @param pdu [out] A buffer to hold the PDU portion of the received packet,
|
||||
* after the BVLC portion has been stripped off.
|
||||
* @param max_pdu [in] Size of the pdu[] buffer.
|
||||
* @param timeout [in] The number of milliseconds to wait for a packet.
|
||||
* @return The number of octets (remaining) in the PDU, or zero on failure.
|
||||
*/
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint16_t bsc_receive(
|
||||
BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu, unsigned timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Function can be used to retrieve broadcast
|
||||
* VMAC address for BACNet/SC node.
|
||||
*
|
||||
* @param addr [out] Value of broadcast VMAC address.
|
||||
*/
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_get_broadcast_address(BACNET_ADDRESS *addr);
|
||||
|
||||
/**
|
||||
* @brief Function can be used to retrieve local
|
||||
* VMAC address of initialized BACNet/SC datalink.
|
||||
* If function called when datalink is not started,
|
||||
* my_address filled by empty vmac address
|
||||
* X'000000000000' as it defined in clause AB.1.5.2
|
||||
*
|
||||
* @param my_address [out] Value of local VMAC address.
|
||||
*/
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_get_my_address(BACNET_ADDRESS *my_address);
|
||||
|
||||
/**
|
||||
* @brief Function checks if BACNet/SC direct connection is
|
||||
* established with remote BACNet/SC node.
|
||||
* User can check the status of connection using either
|
||||
* destination vmac or list of destination urls.
|
||||
* @param dest BACNet/SC vmac of remote node to check direct
|
||||
* connection status.
|
||||
* @param urls this array represents the possible URIs of a
|
||||
* remote node for acceptance of direct connections.
|
||||
* Can contain 1 elem.
|
||||
* @param urls_cnt - size of urls array.
|
||||
* @return true if connection is established otherwise returns
|
||||
* false.
|
||||
*/
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bsc_direct_connection_established(
|
||||
BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt);
|
||||
|
||||
/**
|
||||
* @brief Function starts process of establishing of a
|
||||
* direct BACNet/SC connection to node identified by
|
||||
* either urls or dest parameter. User should note that
|
||||
* if dest parameter is used, local node tries to resolve
|
||||
* it (e.g.to get URIs related to dest vmac from all existent
|
||||
* BACNet/SC nodes in network). As a result the process of
|
||||
* establishing of a BACNet/SC connection by dest may
|
||||
* take unpredictable amount of time depending on a current
|
||||
* network configuration.
|
||||
* @param dest BACNet/SC vmac of remote node to check direct
|
||||
* connection status.
|
||||
* @param urls this array represents the possible URIs of a
|
||||
* remote node for acceptance of direct connections.
|
||||
* Can contain 1 elem.
|
||||
* @param urls_cnt - size of urls array.
|
||||
*
|
||||
* @return BSC_SC_SUCCESS if process of a establishing of a BACNet/SC
|
||||
* connection was started successfully, otherwise returns
|
||||
* any retcode from BSC_SC_RET enum.
|
||||
*/
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET
|
||||
bsc_connect_direct(BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_disconnect_direct(BACNET_SC_VMAC_ADDRESS *dest);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_maintenance_timer(uint16_t seconds);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief crossplatform event abstraction used in BACnet secure connect.
|
||||
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||
* @date August 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_DATALINK_BSC_EVENT_H
|
||||
#define BACNET_DATALINK_BSC_EVENT_H
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
|
||||
struct BSC_Event;
|
||||
typedef struct BSC_Event BSC_EVENT;
|
||||
|
||||
/**
|
||||
* @brief bsc_event_init() allocates and initializes auto-reset
|
||||
* event object to non-signalled state. An event object
|
||||
* can be set to signalled state by the call of
|
||||
* bsc_event_signal(). When the state of the event object
|
||||
* is signaled, it remains signaled until last thread is
|
||||
* released which was blocked on bsc_event_wait() or
|
||||
* bsc_event_timed_wait() calls. When writing the code user
|
||||
* must always remember the following statements:
|
||||
* 1. It is guaranteed that all currently waiting threads will be
|
||||
* unblocked by the call of bsc_event_signal().
|
||||
* 2. If user has called bsc_event_wait() or bsc_event_timedwait()
|
||||
* from thread N after the call of bsc_event_signal() but
|
||||
* when the state of the corresponded event object is still
|
||||
* signalled, it is not guaranteed that thread N will be
|
||||
* unblocked. Thread N may be stay blocked or may unblock
|
||||
* depending on multitasking context of the OS.
|
||||
*
|
||||
* @return handle of event object if function succeeded, otherwise
|
||||
* returns NULL.
|
||||
*/
|
||||
|
||||
BSC_EVENT *bsc_event_init(void);
|
||||
|
||||
/**
|
||||
* @brief bsc_event_deinit() deinitialize auto-reset
|
||||
* event object. If a user calls that function while
|
||||
* some threads are waiting the specified event object ev
|
||||
* the behaviour is undefined.
|
||||
*
|
||||
* @param ev - handle to previously initialized event object.
|
||||
*/
|
||||
|
||||
void bsc_event_deinit(BSC_EVENT *ev);
|
||||
|
||||
/**
|
||||
* @brief bsc_wait() suspends the execution of the current thread
|
||||
* for the specified amount of seconds.
|
||||
*
|
||||
* @param seconds - time to sleep in seconds.
|
||||
*/
|
||||
|
||||
void bsc_wait(int seconds);
|
||||
|
||||
/**
|
||||
* @brief bsc_wait() suspends the execution of the current thread
|
||||
* for the specified amount of seconds.
|
||||
*
|
||||
* @param mseconds - time to sleep in milliseconds.
|
||||
*/
|
||||
|
||||
void bsc_wait_ms(int mseconds);
|
||||
|
||||
/**
|
||||
* @brief bsc_event_wait() suspends the execution of the current thread
|
||||
* until corresponded event object becomes signalled.
|
||||
*
|
||||
* @param ev - handle to previously initialized event object.
|
||||
*/
|
||||
|
||||
void bsc_event_wait(BSC_EVENT *ev);
|
||||
|
||||
/**
|
||||
* @brief bsc_event_timedwait() suspends the execution of the current thread
|
||||
* until corresponded event object becomes signalled
|
||||
* or ms_timeout becomes elapsed.
|
||||
*
|
||||
* @param ev - handle to previously initialized event object.
|
||||
* @param ms_timeout - timeout in milliseconds
|
||||
* @return true if the corresponded event was signalled.
|
||||
* false if ms_timeout was elapsed but event ev is still in
|
||||
* non signalled state.
|
||||
*/
|
||||
|
||||
bool bsc_event_timedwait(BSC_EVENT *ev, unsigned int ms_timeout);
|
||||
|
||||
/**
|
||||
* @brief bsc_event_signal() function sets state of corresponded event object
|
||||
* to signalled state.
|
||||
*
|
||||
* @param ev - handle to previously initialized event object.
|
||||
*/
|
||||
|
||||
void bsc_event_signal(BSC_EVENT *ev);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,651 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief BACnet hub connector API.
|
||||
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||
* @date July 2022
|
||||
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*/
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||
#include "bacnet/datalink/bsc/bsc-socket.h"
|
||||
#include "bacnet/datalink/bsc/bsc-util.h"
|
||||
#include "bacnet/datalink/bsc/bsc-hub-connector.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/basic/object/sc_netport.h"
|
||||
#include "bacnet/bactext.h"
|
||||
|
||||
#define DEBUG_BSC_HUB_CONNECTOR 0
|
||||
|
||||
#if DEBUG_BSC_HUB_CONNECTOR == 1
|
||||
#define DEBUG_PRINTF debug_printf
|
||||
#else
|
||||
#undef DEBUG_ENABLED
|
||||
#define DEBUG_PRINTF debug_printf_disabled
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
BSC_HUB_CONN_PRIMARY = 0,
|
||||
BSC_HUB_CONN_FAILOVER = 1
|
||||
} BSC_HUB_CONN_TYPE;
|
||||
|
||||
static void hub_connector_socket_event(
|
||||
BSC_SOCKET *c,
|
||||
BSC_SOCKET_EVENT ev,
|
||||
BACNET_ERROR_CODE reason,
|
||||
const char *reason_desc,
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
BVLC_SC_DECODED_MESSAGE *decoded_pdu);
|
||||
|
||||
static void hub_connector_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev);
|
||||
|
||||
typedef enum {
|
||||
BSC_HUB_CONNECTOR_STATE_IDLE = 0,
|
||||
BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY = 1,
|
||||
BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER = 2,
|
||||
BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY = 3,
|
||||
BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER = 4,
|
||||
BSC_HUB_CONNECTOR_STATE_WAIT_FOR_RECONNECT = 5,
|
||||
BSC_HUB_CONNECTOR_STATE_WAIT_FOR_CTX_DEINIT = 6,
|
||||
BSC_HUB_CONNECTOR_STATE_DUPLICATED_VMAC = 7
|
||||
} BSC_HUB_CONNECTOR_STATE;
|
||||
|
||||
typedef struct BSC_Hub_Connector {
|
||||
bool used;
|
||||
BSC_SOCKET_CTX ctx;
|
||||
BSC_CONTEXT_CFG cfg;
|
||||
BSC_SOCKET sock[2];
|
||||
BSC_HUB_CONNECTOR_STATE state;
|
||||
unsigned int reconnect_timeout_s;
|
||||
uint8_t primary_url[BSC_WSURL_MAX_LEN + 1];
|
||||
uint8_t failover_url[BSC_WSURL_MAX_LEN + 1];
|
||||
struct mstimer t;
|
||||
BSC_HUB_CONNECTOR_EVENT_FUNC event_func;
|
||||
void *user_arg;
|
||||
BACNET_SC_HUB_CONNECTION_STATUS primary_status;
|
||||
BACNET_SC_HUB_CONNECTION_STATUS failover_status;
|
||||
} BSC_HUB_CONNECTOR;
|
||||
|
||||
#if BSC_CONF_HUB_CONNECTORS_NUM > 0
|
||||
static BSC_HUB_CONNECTOR bsc_hub_connector[BSC_CONF_HUB_CONNECTORS_NUM] = { 0 };
|
||||
#else
|
||||
static BSC_HUB_CONNECTOR *bsc_hub_connector = NULL;
|
||||
#endif
|
||||
|
||||
static BSC_SOCKET_CTX_FUNCS bsc_hub_connector_ctx_funcs = {
|
||||
NULL, NULL, hub_connector_socket_event, hub_connector_context_event, NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize the BACnet hub connector status
|
||||
* @param s - pointer to the status structure
|
||||
*/
|
||||
static void hub_connector_reset_status(BACNET_SC_HUB_CONNECTION_STATUS *s)
|
||||
{
|
||||
/* set timestamps to unspecified values */
|
||||
memset(&s->Connect_Timestamp, 0xFF, sizeof(s->Connect_Timestamp));
|
||||
memset(&s->Disconnect_Timestamp, 0xFF, sizeof(s->Disconnect_Timestamp));
|
||||
s->Error = ERROR_CODE_DEFAULT;
|
||||
s->Error_Details[0] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate a hub connector
|
||||
* @return pointer to the allocated hub connector
|
||||
*/
|
||||
static BSC_HUB_CONNECTOR *hub_connector_alloc(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < BSC_CONF_HUB_CONNECTORS_NUM; i++) {
|
||||
if (!bsc_hub_connector[i].used) {
|
||||
memset(&bsc_hub_connector[i], 0, sizeof(bsc_hub_connector[i]));
|
||||
bsc_hub_connector[i].used = true;
|
||||
hub_connector_reset_status(&bsc_hub_connector[i].primary_status);
|
||||
hub_connector_reset_status(&bsc_hub_connector[i].failover_status);
|
||||
DEBUG_PRINTF(
|
||||
"hub_connector_alloc() ret = %p\n", &bsc_hub_connector[i]);
|
||||
return &bsc_hub_connector[i];
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTF("hub_connector_alloc() ret = %p\n", &bsc_hub_connector[i]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free a hub connector
|
||||
* @param c - pointer to the hub connector
|
||||
*/
|
||||
static void hub_connector_free(BSC_HUB_CONNECTOR *c)
|
||||
{
|
||||
DEBUG_PRINTF("hub_connector_free() c = %p\n", c);
|
||||
c->used = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the BACnet hub connector status
|
||||
* @param s - pointer to the status structure
|
||||
* @param state - new state
|
||||
* @param err - error code
|
||||
* @param err_desc - error description
|
||||
*/
|
||||
static void hub_conector_update_status(
|
||||
BACNET_SC_HUB_CONNECTION_STATUS *s,
|
||||
BACNET_SC_CONNECTION_STATE state,
|
||||
BACNET_ERROR_CODE err,
|
||||
const char *err_desc)
|
||||
{
|
||||
s->State = state;
|
||||
if (state == BACNET_SC_CONNECTION_STATE_NOT_CONNECTED ||
|
||||
state == BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS) {
|
||||
bsc_set_timestamp(&s->Disconnect_Timestamp);
|
||||
} else if (
|
||||
state == BACNET_SC_CONNECTION_STATE_CONNECTED ||
|
||||
state == BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT) {
|
||||
bsc_set_timestamp(&s->Connect_Timestamp);
|
||||
}
|
||||
s->Error = err;
|
||||
s->Error_Details[0] = 0;
|
||||
if (err_desc) {
|
||||
bsc_copy_str(&s->Error_Details[0], err_desc, sizeof(s->Error_Details));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect to a BACnet hub
|
||||
* @param p - pointer to the hub connector
|
||||
* @param type - connection type
|
||||
*/
|
||||
static void hub_connector_connect(BSC_HUB_CONNECTOR *p, BSC_HUB_CONN_TYPE type)
|
||||
{
|
||||
BSC_SC_RET ret;
|
||||
char *url = (type == BSC_HUB_CONN_PRIMARY) ? (char *)p->primary_url
|
||||
: (char *)p->failover_url;
|
||||
|
||||
p->state = (type == BSC_HUB_CONN_PRIMARY)
|
||||
? BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY
|
||||
: BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER;
|
||||
|
||||
DEBUG_PRINTF(
|
||||
"hub_connector_connect() hub = %p connecting to url %s\n", p, url);
|
||||
|
||||
if (url[0] == 0) {
|
||||
mstimer_set(&p->t, p->reconnect_timeout_s * 1000);
|
||||
p->state = BSC_HUB_CONNECTOR_STATE_WAIT_FOR_RECONNECT;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = bsc_connect(&p->ctx, &p->sock[type], url);
|
||||
(void)ret;
|
||||
|
||||
#if DEBUG_ENABLED == 1
|
||||
if (ret != BSC_SC_SUCCESS) {
|
||||
DEBUG_PRINTF(
|
||||
"hub_connector_connect() got error while "
|
||||
"connecting to hub type %d, err = %d\n",
|
||||
type, ret);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process the hub connector state
|
||||
* @param c - pointer to the hub connector
|
||||
*/
|
||||
static void hub_connector_process_state(BSC_HUB_CONNECTOR *c)
|
||||
{
|
||||
if (c->state == BSC_HUB_CONNECTOR_STATE_WAIT_FOR_RECONNECT) {
|
||||
if (mstimer_expired(&c->t)) {
|
||||
hub_connector_connect(c, BSC_HUB_CONN_PRIMARY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Hub connector maintenance timer
|
||||
* @param seconds - number of elapsed seconds
|
||||
*/
|
||||
void bsc_hub_connector_maintenance_timer(uint16_t seconds)
|
||||
{
|
||||
int i;
|
||||
(void)seconds;
|
||||
|
||||
bws_dispatch_lock();
|
||||
for (i = 0; i < BSC_CONF_HUB_CONNECTORS_NUM; i++) {
|
||||
if (bsc_hub_connector[i].used) {
|
||||
hub_connector_process_state(&bsc_hub_connector[i]);
|
||||
}
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Hub connector socket event
|
||||
* @param c - pointer to the socket
|
||||
* @param ev - event
|
||||
* @param disconnect_reason - disconnect reason
|
||||
* @param disconnect_reason_desc - disconnect reason description
|
||||
* @param pdu - pointer to the PDU
|
||||
* @param pdu_len - PDU length
|
||||
* @param decoded_pdu - decoded PDU
|
||||
*/
|
||||
static void hub_connector_socket_event(
|
||||
BSC_SOCKET *c,
|
||||
BSC_SOCKET_EVENT ev,
|
||||
BACNET_ERROR_CODE disconnect_reason,
|
||||
const char *disconnect_reason_desc,
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
BVLC_SC_DECODED_MESSAGE *decoded_pdu)
|
||||
{
|
||||
BSC_HUB_CONNECTOR *hc;
|
||||
BACNET_SC_CONNECTION_STATE st =
|
||||
BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS;
|
||||
|
||||
bws_dispatch_lock();
|
||||
hc = (BSC_HUB_CONNECTOR *)c->ctx->user_arg;
|
||||
DEBUG_PRINTF(
|
||||
"hub_connector_socket_event() >>> hub_connector = %p, socket "
|
||||
"= %p, ev = %d, reason = %d, reason_desc = %p,"
|
||||
"pdu = %p, pdu_len = %d\n",
|
||||
hc, c, ev, disconnect_reason, disconnect_reason_desc, pdu, pdu_len);
|
||||
DEBUG_PRINTF("hub_connector_socket_event() state = %d\n", hc->state);
|
||||
if (ev == BSC_SOCKET_EVENT_CONNECTED) {
|
||||
if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY) {
|
||||
DEBUG_PRINTF(
|
||||
"hub_connector_socket_event() hub_connector = %p "
|
||||
"connected primary\n",
|
||||
hc);
|
||||
hc->state = BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY;
|
||||
hub_conector_update_status(
|
||||
&hc->primary_status, BACNET_SC_CONNECTION_STATE_CONNECTED,
|
||||
ERROR_CODE_DEFAULT, NULL);
|
||||
hc->event_func(
|
||||
BSC_HUBC_EVENT_CONNECTED_PRIMARY, hc, hc->user_arg, NULL, 0,
|
||||
NULL);
|
||||
} else if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER) {
|
||||
DEBUG_PRINTF(
|
||||
"hub_connector_socket_event() hub_connector = %p "
|
||||
"connected failover\n",
|
||||
hc);
|
||||
hc->state = BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER;
|
||||
hub_conector_update_status(
|
||||
&hc->failover_status, BACNET_SC_CONNECTION_STATE_CONNECTED,
|
||||
ERROR_CODE_DEFAULT, NULL);
|
||||
hc->event_func(
|
||||
BSC_HUBC_EVENT_CONNECTED_FAILOVER, hc, hc->user_arg, NULL, 0,
|
||||
NULL);
|
||||
}
|
||||
} else if (ev == BSC_SOCKET_EVENT_DISCONNECTED) {
|
||||
if (disconnect_reason == ERROR_CODE_NODE_DUPLICATE_VMAC &&
|
||||
hc->state != BSC_HUB_CONNECTOR_STATE_WAIT_FOR_CTX_DEINIT) {
|
||||
DEBUG_PRINTF("hub_connector_socket_event() "
|
||||
"got ERROR_CODE_NODE_DUPLICATE_VMAC error\n");
|
||||
if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY) {
|
||||
hub_conector_update_status(
|
||||
&hc->primary_status,
|
||||
BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT,
|
||||
disconnect_reason, disconnect_reason_desc);
|
||||
} else if (
|
||||
hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER) {
|
||||
hub_conector_update_status(
|
||||
&hc->failover_status,
|
||||
BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT,
|
||||
disconnect_reason, disconnect_reason_desc);
|
||||
}
|
||||
hc->state = BSC_HUB_CONNECTOR_STATE_DUPLICATED_VMAC;
|
||||
hc->event_func(
|
||||
BSC_HUBC_EVENT_ERROR_DUPLICATED_VMAC, hc, hc->user_arg, NULL, 0,
|
||||
NULL);
|
||||
} else if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY) {
|
||||
DEBUG_PRINTF("hub_connector_socket_event() try to connect to "
|
||||
"failover hub\n");
|
||||
hub_conector_update_status(
|
||||
&hc->primary_status,
|
||||
BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT, disconnect_reason,
|
||||
disconnect_reason_desc);
|
||||
hub_connector_connect(hc, BSC_HUB_CONN_FAILOVER);
|
||||
} else if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER) {
|
||||
DEBUG_PRINTF(
|
||||
"hub_connector_socket_event() wait for %d seconds\n",
|
||||
hc->reconnect_timeout_s);
|
||||
hub_conector_update_status(
|
||||
&hc->failover_status,
|
||||
BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT, disconnect_reason,
|
||||
disconnect_reason_desc);
|
||||
hc->state = BSC_HUB_CONNECTOR_STATE_WAIT_FOR_RECONNECT;
|
||||
mstimer_set(&hc->t, hc->reconnect_timeout_s * 1000);
|
||||
} else if (
|
||||
hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY ||
|
||||
hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER) {
|
||||
if (disconnect_reason == ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER ||
|
||||
disconnect_reason == ERROR_CODE_SUCCESS) {
|
||||
st = BACNET_SC_CONNECTION_STATE_NOT_CONNECTED;
|
||||
}
|
||||
if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY) {
|
||||
hub_conector_update_status(
|
||||
&hc->primary_status, st, ERROR_CODE_DEFAULT, NULL);
|
||||
} else {
|
||||
hub_conector_update_status(
|
||||
&hc->failover_status, st, ERROR_CODE_DEFAULT, NULL);
|
||||
}
|
||||
DEBUG_PRINTF(
|
||||
"hub_connector_socket_event() try to connect to primary hub\n");
|
||||
hub_connector_connect(hc, BSC_HUB_CONN_PRIMARY);
|
||||
}
|
||||
} else if (ev == BSC_SOCKET_EVENT_RECEIVED) {
|
||||
DEBUG_PRINTF(
|
||||
"hub_connector_socket_event() hub_connector = %p pdu of "
|
||||
"%d len is received\n",
|
||||
hc, pdu_len);
|
||||
hc->event_func(
|
||||
BSC_HUBC_EVENT_RECEIVED, hc, hc->user_arg, pdu, pdu_len,
|
||||
decoded_pdu);
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("hub_connector_socket_event() <<<\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Hub connector context event
|
||||
* @param ctx - pointer to the context
|
||||
* @param ev - event
|
||||
*/
|
||||
static void hub_connector_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev)
|
||||
{
|
||||
BSC_HUB_CONNECTOR *c;
|
||||
|
||||
DEBUG_PRINTF(
|
||||
"hub_connector_context_event() >>> ctx = %p, ev = %d\n", ctx, ev);
|
||||
|
||||
if (ev == BSC_CTX_DEINITIALIZED) {
|
||||
bws_dispatch_lock();
|
||||
c = (BSC_HUB_CONNECTOR *)ctx->user_arg;
|
||||
if (c->state != BSC_HUB_CONNECTOR_STATE_IDLE) {
|
||||
c->state = BSC_HUB_CONNECTOR_STATE_IDLE;
|
||||
hub_connector_free(c);
|
||||
c->event_func(
|
||||
BSC_HUBC_EVENT_STOPPED, c, c->user_arg, NULL, 0, NULL);
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
}
|
||||
|
||||
DEBUG_PRINTF("hub_connector_context_event() <<<\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start a BACnet hub connector
|
||||
* @param ca_cert_chain - pointer to the CA certificate chain
|
||||
* @param ca_cert_chain_size - size of the CA certificate chain
|
||||
* @param cert_chain - pointer to the certificate chain
|
||||
* @param cert_chain_size - size of the certificate chain
|
||||
* @param key - pointer to the private key
|
||||
* @param key_size - size of the private key
|
||||
* @param local_uuid - pointer to the local UUID
|
||||
* @param local_vmac - pointer to the local VMAC address
|
||||
* @param max_local_bvlc_len - maximum BVLC message size
|
||||
* @param max_local_npdu_len - maximum NPDU message size
|
||||
* @param connect_timeout_s - connection timeout in seconds
|
||||
* @param heartbeat_timeout_s - heartbeat timeout in seconds
|
||||
* @param disconnect_timeout_s - disconnect timeout in seconds
|
||||
* @param primaryURL - primary hub URL
|
||||
* @param failoverURL - failover hub URL
|
||||
* @param reconnect_timeout_s - reconnect timeout in seconds
|
||||
* @param event_func - event function
|
||||
* @param user_arg - user argument
|
||||
* @param h - pointer to the hub connector handle
|
||||
* @return status
|
||||
*/
|
||||
BSC_SC_RET bsc_hub_connector_start(
|
||||
uint8_t *ca_cert_chain,
|
||||
size_t ca_cert_chain_size,
|
||||
uint8_t *cert_chain,
|
||||
size_t cert_chain_size,
|
||||
uint8_t *key,
|
||||
size_t key_size,
|
||||
BACNET_SC_UUID *local_uuid,
|
||||
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||
uint16_t max_local_bvlc_len,
|
||||
uint16_t max_local_npdu_len,
|
||||
unsigned int connect_timeout_s,
|
||||
unsigned int heartbeat_timeout_s,
|
||||
unsigned int disconnect_timeout_s,
|
||||
char *primaryURL,
|
||||
char *failoverURL,
|
||||
unsigned int reconnect_timeout_s,
|
||||
BSC_HUB_CONNECTOR_EVENT_FUNC event_func,
|
||||
void *user_arg,
|
||||
BSC_HUB_CONNECTOR_HANDLE *h)
|
||||
{
|
||||
BSC_SC_RET ret = BSC_SC_SUCCESS;
|
||||
BSC_HUB_CONNECTOR *c;
|
||||
|
||||
DEBUG_PRINTF("bsc_hub_connector_start() >>>\n");
|
||||
|
||||
if (!ca_cert_chain || !ca_cert_chain_size || !cert_chain ||
|
||||
!cert_chain_size || !key || !key_size || !local_uuid || !local_vmac ||
|
||||
!max_local_npdu_len || !max_local_bvlc_len || !connect_timeout_s ||
|
||||
!heartbeat_timeout_s || !disconnect_timeout_s || !primaryURL ||
|
||||
!reconnect_timeout_s || !event_func || !h) {
|
||||
DEBUG_PRINTF("bsc_hub_connector_start() <<< ret = BSC_SC_BAD_PARAM\n");
|
||||
return BSC_SC_BAD_PARAM;
|
||||
}
|
||||
|
||||
if (strlen(primaryURL) > BSC_WSURL_MAX_LEN ||
|
||||
(failoverURL && (strlen(failoverURL) > BSC_WSURL_MAX_LEN))) {
|
||||
DEBUG_PRINTF("bsc_hub_connector_start() <<< ret = BSC_SC_BAD_PARAM\n");
|
||||
return BSC_SC_BAD_PARAM;
|
||||
}
|
||||
|
||||
bws_dispatch_lock();
|
||||
c = hub_connector_alloc();
|
||||
if (!c) {
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF(
|
||||
"bsc_hub_connector_start() <<< ret = BSC_SC_NO_RESOURCES\n");
|
||||
return BSC_SC_NO_RESOURCES;
|
||||
}
|
||||
|
||||
c->reconnect_timeout_s = reconnect_timeout_s;
|
||||
c->primary_url[0] = 0;
|
||||
c->failover_url[0] = 0;
|
||||
c->user_arg = user_arg;
|
||||
strcpy((char *)c->primary_url, primaryURL);
|
||||
|
||||
if (failoverURL) {
|
||||
strcpy((char *)c->failover_url, failoverURL);
|
||||
}
|
||||
|
||||
c->event_func = event_func;
|
||||
|
||||
bsc_init_ctx_cfg(
|
||||
BSC_SOCKET_CTX_INITIATOR, &c->cfg, BSC_WEBSOCKET_HUB_PROTOCOL, 0, NULL,
|
||||
ca_cert_chain, ca_cert_chain_size, cert_chain, cert_chain_size, key,
|
||||
key_size, local_uuid, local_vmac, max_local_bvlc_len,
|
||||
max_local_npdu_len, connect_timeout_s, heartbeat_timeout_s,
|
||||
disconnect_timeout_s);
|
||||
|
||||
DEBUG_PRINTF(
|
||||
"bsc_hub_connector_start() uuid = %s, vmac = %s\n",
|
||||
bsc_uuid_to_string(&c->cfg.local_uuid),
|
||||
bsc_vmac_to_string(&c->cfg.local_vmac));
|
||||
|
||||
ret = bsc_init_ctx(
|
||||
&c->ctx, &c->cfg, &bsc_hub_connector_ctx_funcs, c->sock,
|
||||
sizeof(c->sock) / sizeof(BSC_SOCKET), (void *)c);
|
||||
|
||||
if (ret == BSC_SC_SUCCESS) {
|
||||
*h = (BSC_HUB_CONNECTOR_HANDLE)c;
|
||||
DEBUG_PRINTF(
|
||||
"bsc_hub_connector_start() hub = %p connecting to url %s\n", c,
|
||||
c->primary_url);
|
||||
ret = bsc_connect(
|
||||
&c->ctx, &c->sock[BSC_HUB_CONN_PRIMARY], (char *)c->primary_url);
|
||||
if (ret == BSC_SC_SUCCESS) {
|
||||
c->state = BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY;
|
||||
} else {
|
||||
if (c->failover_url[0] == 0) {
|
||||
c->state = BSC_HUB_CONNECTOR_STATE_IDLE;
|
||||
bsc_deinit_ctx(&c->ctx);
|
||||
hub_connector_free(c);
|
||||
*h = NULL;
|
||||
} else {
|
||||
c->state = BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER;
|
||||
DEBUG_PRINTF(
|
||||
"bsc_hub_connector_start() hub = %p connecting to url %s\n",
|
||||
c, c->primary_url);
|
||||
ret = bsc_connect(
|
||||
&c->ctx, &c->sock[BSC_HUB_CONN_FAILOVER],
|
||||
(char *)c->failover_url);
|
||||
if (ret != BSC_SC_SUCCESS) {
|
||||
c->state = BSC_HUB_CONNECTOR_STATE_IDLE;
|
||||
bsc_deinit_ctx(&c->ctx);
|
||||
hub_connector_free(c);
|
||||
*h = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_hub_connector_start() <<< ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stop a BACnet hub connector
|
||||
* @param h - pointer to the hub connector
|
||||
*/
|
||||
void bsc_hub_connector_stop(BSC_HUB_CONNECTOR_HANDLE h)
|
||||
{
|
||||
BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h;
|
||||
DEBUG_PRINTF("bsc_hub_connector_stop() >>> h = %p\n", h);
|
||||
|
||||
bws_dispatch_lock();
|
||||
|
||||
#if DEBUG_ENABLED == 1
|
||||
if (c) {
|
||||
DEBUG_PRINTF("bsc_hub_connector_stop() state = %d\n", c->state);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (c && c->state != BSC_HUB_CONNECTOR_STATE_WAIT_FOR_CTX_DEINIT &&
|
||||
c->state != BSC_HUB_CONNECTOR_STATE_IDLE) {
|
||||
c->state = BSC_HUB_CONNECTOR_STATE_WAIT_FOR_CTX_DEINIT;
|
||||
bsc_deinit_ctx(&c->ctx);
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_hub_connector_stop() <<<\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send a BACnet hub connector PDU
|
||||
* @param h - pointer to the hub connector
|
||||
* @param pdu - pointer to the PDU
|
||||
* @param pdu_len - PDU length
|
||||
* @return status
|
||||
*/
|
||||
BSC_SC_RET
|
||||
bsc_hub_connector_send(BSC_HUB_CONNECTOR_HANDLE h, uint8_t *pdu, size_t pdu_len)
|
||||
{
|
||||
BSC_SC_RET ret;
|
||||
BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h;
|
||||
|
||||
DEBUG_PRINTF(
|
||||
"bsc_hub_connector_send() >>> h = %p, pdu = %p, pdu_len = %d\n", h,
|
||||
pdu, pdu_len);
|
||||
|
||||
bws_dispatch_lock();
|
||||
if (!c) {
|
||||
DEBUG_PRINTF("bsc_hub_connector_send() <<< ret = BSC_SC_BAD_PARAM\n");
|
||||
bws_dispatch_unlock();
|
||||
return BSC_SC_BAD_PARAM;
|
||||
}
|
||||
if (c->state == BSC_HUB_CONNECTOR_STATE_IDLE ||
|
||||
(c->state != BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY &&
|
||||
c->state != BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER)) {
|
||||
DEBUG_PRINTF(
|
||||
"bsc_hub_connector_send() pdu is dropped, state of "
|
||||
"hub_connector %p is %d\n",
|
||||
c, c->state);
|
||||
DEBUG_PRINTF(
|
||||
"bsc_hub_connector_send() <<< ret = BSC_SC_INVALID_OPERATION\n");
|
||||
bws_dispatch_unlock();
|
||||
return BSC_SC_INVALID_OPERATION;
|
||||
}
|
||||
if (c->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY) {
|
||||
ret = bsc_send(&c->sock[BSC_HUB_CONN_PRIMARY], pdu, pdu_len);
|
||||
} else {
|
||||
ret = bsc_send(&c->sock[BSC_HUB_CONN_FAILOVER], pdu, pdu_len);
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_hub_connector_send() <<< ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the hub connector is stopped
|
||||
* @param h - pointer to the hub connector
|
||||
* @return status
|
||||
*/
|
||||
bool bsc_hub_connector_stopped(BSC_HUB_CONNECTOR_HANDLE h)
|
||||
{
|
||||
BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h;
|
||||
bool ret = false;
|
||||
|
||||
DEBUG_PRINTF("bsc_hub_connector_stopped() >>> h = %p\n", h);
|
||||
bws_dispatch_lock();
|
||||
if (c && c->state == BSC_HUB_CONNECTOR_STATE_IDLE) {
|
||||
ret = true;
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_hub_connector_stopped() <<< ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the hub connector status
|
||||
* @param h - pointer to the hub connector
|
||||
* @param primary - primary or failover status
|
||||
* @return pointer to the status
|
||||
*/
|
||||
BACNET_SC_HUB_CONNECTION_STATUS *
|
||||
bsc_hub_connector_status(BSC_HUB_CONNECTOR_HANDLE h, bool primary)
|
||||
{
|
||||
BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h;
|
||||
BACNET_SC_HUB_CONNECTION_STATUS *ret = NULL;
|
||||
bws_dispatch_lock();
|
||||
if (c) {
|
||||
if (primary) {
|
||||
ret = &c->primary_status;
|
||||
} else {
|
||||
ret = &c->failover_status;
|
||||
}
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the hub connector state
|
||||
* @param h - pointer to the hub connector
|
||||
* @return state
|
||||
*/
|
||||
BACNET_SC_HUB_CONNECTOR_STATE
|
||||
bsc_hub_connector_state(BSC_HUB_CONNECTOR_HANDLE h)
|
||||
{
|
||||
BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h;
|
||||
BACNET_SC_HUB_CONNECTOR_STATE ret =
|
||||
BACNET_SC_HUB_CONNECTOR_STATE_NO_HUB_CONNECTION;
|
||||
bws_dispatch_lock();
|
||||
if (c) {
|
||||
if (c->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY) {
|
||||
ret = BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_PRIMARY;
|
||||
} else if (c->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER) {
|
||||
ret = BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_FAILOVER;
|
||||
}
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief BACNet secure connect hub connector API.
|
||||
* In general, user should not use that API directly,
|
||||
* BACNet/SC datalink API should be used.
|
||||
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||
* @date July 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_DATALINK_BSC_HUB_CONNECTOR_H
|
||||
#define BACNET_DATALINK_BSC_HUB_CONNECTOR_H
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||
|
||||
typedef void *BSC_HUB_CONNECTOR_HANDLE;
|
||||
|
||||
typedef enum {
|
||||
BSC_HUBC_EVENT_CONNECTED_PRIMARY = 1,
|
||||
BSC_HUBC_EVENT_CONNECTED_FAILOVER = 2,
|
||||
BSC_HUBC_EVENT_ERROR_DUPLICATED_VMAC = 3,
|
||||
BSC_HUBC_EVENT_RECEIVED = 4,
|
||||
BSC_HUBC_EVENT_STOPPED = 5
|
||||
} BSC_HUB_CONNECTOR_EVENT;
|
||||
|
||||
typedef void (*BSC_HUB_CONNECTOR_EVENT_FUNC)(
|
||||
BSC_HUB_CONNECTOR_EVENT ev,
|
||||
BSC_HUB_CONNECTOR_HANDLE h,
|
||||
void *user_arg,
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
BVLC_SC_DECODED_MESSAGE *decoded_pdu);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_hub_connector_start(
|
||||
uint8_t *ca_cert_chain,
|
||||
size_t ca_cert_chain_size,
|
||||
uint8_t *cert_chain,
|
||||
size_t cert_chain_size,
|
||||
uint8_t *key,
|
||||
size_t key_size,
|
||||
BACNET_SC_UUID *local_uuid,
|
||||
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||
uint16_t max_local_bvlc_len,
|
||||
uint16_t max_local_npdu_len,
|
||||
unsigned int connect_timeout_s,
|
||||
unsigned int heartbeat_timeout_s,
|
||||
unsigned int disconnect_timeout_s,
|
||||
char *primaryURL,
|
||||
char *failoverURL,
|
||||
unsigned int reconnnect_timeout_s,
|
||||
BSC_HUB_CONNECTOR_EVENT_FUNC event_func,
|
||||
void *user_arg,
|
||||
BSC_HUB_CONNECTOR_HANDLE *h);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_hub_connector_stop(BSC_HUB_CONNECTOR_HANDLE h);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_hub_connector_send(
|
||||
BSC_HUB_CONNECTOR_HANDLE h, uint8_t *pdu, size_t pdu_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bsc_hub_connector_stopped(BSC_HUB_CONNECTOR_HANDLE h);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_HUB_CONNECTOR_STATE
|
||||
bsc_hub_connector_state(BSC_HUB_CONNECTOR_HANDLE h);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_HUB_CONNECTION_STATUS *
|
||||
bsc_hub_connector_status(BSC_HUB_CONNECTOR_HANDLE h, bool primary);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_hub_connector_maintenance_timer(uint16_t seconds);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,535 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief BACNet hub function API.
|
||||
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||
* @date July 2022
|
||||
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*/
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||
#include "bacnet/datalink/bsc/bsc-socket.h"
|
||||
#include "bacnet/datalink/bsc/bsc-util.h"
|
||||
#include "bacnet/datalink/bsc/bsc-hub-function.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
|
||||
#define DEBUG_BSC_HUB_FUNCTION 0
|
||||
|
||||
#if DEBUG_BSC_HUB_FUNCTION == 1
|
||||
#define DEBUG_PRINTF debug_printf
|
||||
#else
|
||||
#undef DEBUG_ENABLED
|
||||
#define DEBUG_PRINTF debug_printf_disabled
|
||||
#endif
|
||||
|
||||
static BSC_SOCKET *hub_function_find_connection_for_vmac(
|
||||
BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg);
|
||||
|
||||
static BSC_SOCKET *
|
||||
hub_function_find_connection_for_uuid(BACNET_SC_UUID *uuid, void *user_arg);
|
||||
|
||||
static void hub_function_failed_request(
|
||||
BSC_SOCKET_CTX *ctx,
|
||||
BSC_SOCKET *c,
|
||||
BACNET_SC_VMAC_ADDRESS *vmac,
|
||||
BACNET_SC_UUID *uuid,
|
||||
BACNET_ERROR_CODE error,
|
||||
const char *error_desc);
|
||||
|
||||
static void hub_function_socket_event(
|
||||
BSC_SOCKET *c,
|
||||
BSC_SOCKET_EVENT ev,
|
||||
BACNET_ERROR_CODE reason,
|
||||
const char *reason_desc,
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
BVLC_SC_DECODED_MESSAGE *decoded_pdu);
|
||||
|
||||
static void hub_function_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev);
|
||||
|
||||
typedef enum {
|
||||
BSC_HUB_FUNCTION_STATE_IDLE = 0,
|
||||
BSC_HUB_FUNCTION_STATE_STARTING = 1,
|
||||
BSC_HUB_FUNCTION_STATE_STARTED = 2,
|
||||
BSC_HUB_FUNCTION_STATE_STOPPING = 3
|
||||
} BSC_HUB_FUNCTION_STATE;
|
||||
|
||||
typedef struct BSC_Hub_Connector {
|
||||
bool used;
|
||||
BSC_SOCKET_CTX ctx;
|
||||
BSC_CONTEXT_CFG cfg;
|
||||
BSC_SOCKET sock[BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM];
|
||||
BSC_HUB_FUNCTION_STATE state;
|
||||
BSC_HUB_EVENT_FUNC event_func;
|
||||
void *user_arg;
|
||||
} BSC_HUB_FUNCTION;
|
||||
|
||||
#if BSC_CONF_HUB_FUNCTIONS_NUM > 0
|
||||
static BSC_HUB_FUNCTION bsc_hub_function[BSC_CONF_HUB_FUNCTIONS_NUM] = { 0 };
|
||||
#else
|
||||
static BSC_HUB_FUNCTION *bsc_hub_function = NULL;
|
||||
#endif
|
||||
|
||||
static BSC_SOCKET_CTX_FUNCS bsc_hub_function_ctx_funcs = {
|
||||
hub_function_find_connection_for_vmac,
|
||||
hub_function_find_connection_for_uuid, hub_function_socket_event,
|
||||
hub_function_context_event, hub_function_failed_request
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Allocate a hub function
|
||||
* @return pointer to the hub function
|
||||
*/
|
||||
static BSC_HUB_FUNCTION *hub_function_alloc(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < BSC_CONF_HUB_FUNCTIONS_NUM; i++) {
|
||||
if (!bsc_hub_function[i].used) {
|
||||
bsc_hub_function[i].used = true;
|
||||
return &bsc_hub_function[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free a hub function
|
||||
* @param p - pointer to the hub function
|
||||
*/
|
||||
static void hub_function_free(BSC_HUB_FUNCTION *p)
|
||||
{
|
||||
p->used = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief find a hub function connection for a specific VMAC address
|
||||
* @param vmac - pointer to the VMAC address
|
||||
* @param user_arg - pointer to the user argument
|
||||
* @return pointer to the socket, or NULL if not found
|
||||
*/
|
||||
static BSC_SOCKET *hub_function_find_connection_for_vmac(
|
||||
BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg)
|
||||
{
|
||||
int i;
|
||||
BSC_HUB_FUNCTION *f;
|
||||
|
||||
bws_dispatch_lock();
|
||||
f = (BSC_HUB_FUNCTION *)user_arg;
|
||||
DEBUG_PRINTF(
|
||||
"hubf = %p local_vmac = %s\n", f,
|
||||
bsc_vmac_to_string(&f->cfg.local_vmac));
|
||||
for (i = 0; i < sizeof(f->sock) / sizeof(BSC_SOCKET); i++) {
|
||||
DEBUG_PRINTF(
|
||||
"hubf = %p, sock %p, state = %d, vmac = %s\n", f, &f->sock[i],
|
||||
f->sock[i].state, bsc_vmac_to_string(&f->sock[i].vmac));
|
||||
if (f->sock[i].state != BSC_SOCK_STATE_IDLE &&
|
||||
!memcmp(
|
||||
&vmac->address[0], &f->sock[i].vmac.address[0],
|
||||
sizeof(vmac->address))) {
|
||||
bws_dispatch_unlock();
|
||||
return &f->sock[i];
|
||||
}
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief find a hub function connection for a specific UUID
|
||||
* @param uuid - pointer to the UUID
|
||||
* @param user_arg - pointer to the user argument
|
||||
* @return pointer to the socket, or NULL if not found
|
||||
*/
|
||||
static BSC_SOCKET *
|
||||
hub_function_find_connection_for_uuid(BACNET_SC_UUID *uuid, void *user_arg)
|
||||
{
|
||||
int i;
|
||||
BSC_HUB_FUNCTION *f;
|
||||
|
||||
bws_dispatch_lock();
|
||||
f = (BSC_HUB_FUNCTION *)user_arg;
|
||||
for (i = 0; i < sizeof(f->sock) / sizeof(BSC_SOCKET); i++) {
|
||||
DEBUG_PRINTF(
|
||||
"hubf = %p, sock %p, state = %d, uuid = %s\n", f, &f->sock[i],
|
||||
f->sock[i].state, bsc_uuid_to_string(&f->sock[i].uuid));
|
||||
if (f->sock[i].state != BSC_SOCK_STATE_IDLE &&
|
||||
!memcmp(
|
||||
&uuid->uuid[0], &f->sock[i].uuid.uuid[0], sizeof(uuid->uuid))) {
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("found socket\n");
|
||||
return &f->sock[i];
|
||||
}
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief update the status of the hub function
|
||||
* @param f - pointer to the hub function
|
||||
* @param c - pointer to the socket
|
||||
* @param ev - event
|
||||
* @param disconnect_reason - disconnect reason
|
||||
* @param disconnect_reason_desc - disconnect reason description
|
||||
*/
|
||||
static void hub_function_update_status(
|
||||
BSC_HUB_FUNCTION *f,
|
||||
BSC_SOCKET *c,
|
||||
BSC_SOCKET_EVENT ev,
|
||||
BACNET_ERROR_CODE disconnect_reason,
|
||||
const char *disconnect_reason_desc)
|
||||
{
|
||||
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *s;
|
||||
|
||||
if (f->user_arg) {
|
||||
s = bsc_node_find_hub_status_for_vmac(f->user_arg, &c->vmac);
|
||||
|
||||
if (s) {
|
||||
memcpy(s->Peer_VMAC, &c->vmac.address[0], BVLC_SC_VMAC_SIZE);
|
||||
memcpy(
|
||||
&s->Peer_UUID.uuid.uuid128[0], &c->uuid.uuid[0],
|
||||
BVLC_SC_UUID_SIZE);
|
||||
if (!bsc_socket_get_peer_addr(c, &s->Peer_Address)) {
|
||||
memset(&s->Peer_Address, 0, sizeof(s->Peer_Address));
|
||||
}
|
||||
if (disconnect_reason_desc) {
|
||||
bsc_copy_str(
|
||||
s->Error_Details, disconnect_reason_desc,
|
||||
sizeof(s->Error_Details));
|
||||
} else {
|
||||
s->Error_Details[0] = 0;
|
||||
}
|
||||
s->Error = ERROR_CODE_DEFAULT;
|
||||
if (ev == BSC_SOCKET_EVENT_CONNECTED) {
|
||||
s->State = BACNET_SC_CONNECTION_STATE_CONNECTED;
|
||||
bsc_set_timestamp(&s->Connect_Timestamp);
|
||||
memset(
|
||||
&s->Disconnect_Timestamp, 0xff,
|
||||
sizeof(s->Disconnect_Timestamp));
|
||||
} else if (ev == BSC_SOCKET_EVENT_DISCONNECTED) {
|
||||
bsc_set_timestamp(&s->Disconnect_Timestamp);
|
||||
if (disconnect_reason == ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER ||
|
||||
disconnect_reason == ERROR_CODE_SUCCESS) {
|
||||
s->State = BACNET_SC_CONNECTION_STATE_NOT_CONNECTED;
|
||||
} else {
|
||||
s->State =
|
||||
BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS;
|
||||
s->Error = disconnect_reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle a hub function failed request
|
||||
* @param ctx - pointer to the socket context
|
||||
* @param c - pointer to the socket
|
||||
* @param vmac - pointer to the VMAC address
|
||||
* @param uuid - pointer to the UUID
|
||||
* @param error - error code
|
||||
* @param error_desc - pointer to the error description
|
||||
*/
|
||||
static void hub_function_failed_request(
|
||||
BSC_SOCKET_CTX *ctx,
|
||||
BSC_SOCKET *c,
|
||||
BACNET_SC_VMAC_ADDRESS *vmac,
|
||||
BACNET_SC_UUID *uuid,
|
||||
BACNET_ERROR_CODE error,
|
||||
const char *error_desc)
|
||||
{
|
||||
BSC_HUB_FUNCTION *f;
|
||||
BACNET_HOST_N_PORT_DATA peer;
|
||||
(void)ctx;
|
||||
|
||||
bws_dispatch_lock();
|
||||
f = (BSC_HUB_FUNCTION *)c->ctx->user_arg;
|
||||
if (f->user_arg) {
|
||||
if (bsc_socket_get_peer_addr(c, &peer)) {
|
||||
bsc_node_store_failed_request_info(
|
||||
(BSC_NODE *)f->user_arg, &peer, vmac, uuid, error, error_desc);
|
||||
}
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle a hub function socket event
|
||||
* @param c - pointer to the socket
|
||||
* @param ev - event
|
||||
* @param reason - disconnect reason
|
||||
* @param reason_desc - disconnect reason description
|
||||
* @param pdu - pointer to the PDU
|
||||
* @param pdu_len - PDU length
|
||||
* @param decoded_pdu - decoded PDU
|
||||
*/
|
||||
static void hub_function_socket_event(
|
||||
BSC_SOCKET *c,
|
||||
BSC_SOCKET_EVENT ev,
|
||||
BACNET_ERROR_CODE reason,
|
||||
const char *reason_desc,
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
BVLC_SC_DECODED_MESSAGE *decoded_pdu)
|
||||
{
|
||||
BSC_SOCKET *dst;
|
||||
BSC_SC_RET ret;
|
||||
int i;
|
||||
uint8_t *p_pdu;
|
||||
BSC_HUB_FUNCTION *f;
|
||||
size_t len;
|
||||
|
||||
DEBUG_PRINTF(
|
||||
"hub_function_socket_event() >>> c = %p, ev = %d, reason = "
|
||||
"%d, desc = %p, pdu = %p, pdu_len = %d, decoded_pdu = %p\n",
|
||||
c, ev, reason, reason_desc, pdu, pdu_len, decoded_pdu);
|
||||
|
||||
bws_dispatch_lock();
|
||||
f = (BSC_HUB_FUNCTION *)c->ctx->user_arg;
|
||||
if (ev == BSC_SOCKET_EVENT_RECEIVED) {
|
||||
/* double check that received message does not contain */
|
||||
/* originating virtual address and contains dest vaddr */
|
||||
/* although such kind of check is already in bsc-socket.c */
|
||||
if (!decoded_pdu->hdr.origin && decoded_pdu->hdr.dest) {
|
||||
if (bvlc_sc_is_vmac_broadcast(decoded_pdu->hdr.dest)) {
|
||||
if (bsc_socket_get_global_buf_size() >= pdu_len) {
|
||||
p_pdu = bsc_socket_get_global_buf();
|
||||
len = pdu_len;
|
||||
memcpy(p_pdu, pdu, len);
|
||||
|
||||
for (i = 0; i < sizeof(f->sock) / sizeof(BSC_SOCKET); i++) {
|
||||
if (&f->sock[i] != c &&
|
||||
f->sock[i].state == BSC_SOCK_STATE_CONNECTED) {
|
||||
/* change origin address if presented or add origin
|
||||
*/
|
||||
/* address into pdu by extending of it's header */
|
||||
len = (uint16_t)bvlc_sc_set_orig(
|
||||
&p_pdu, len, &c->vmac);
|
||||
ret = bsc_send(&f->sock[i], p_pdu, len);
|
||||
(void)ret;
|
||||
#if DEBUG_ENABLED == 1
|
||||
if (ret != BSC_SC_SUCCESS) {
|
||||
DEBUG_PRINTF(
|
||||
"sending of reconstructed pdu failed, "
|
||||
"err = %d\n",
|
||||
ret);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#if DEBUG_ENABLED == 1
|
||||
else {
|
||||
DEBUG_PRINTF("pdu with len = %d is dropped\n", pdu_len);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
dst = hub_function_find_connection_for_vmac(
|
||||
decoded_pdu->hdr.dest, (void *)f);
|
||||
if (!dst) {
|
||||
DEBUG_PRINTF(
|
||||
"can not find socket, hub dropped pdu of size "
|
||||
"%d for dest vmac %s\n",
|
||||
pdu_len, bsc_vmac_to_string(decoded_pdu->hdr.dest));
|
||||
} else {
|
||||
bvlc_sc_remove_dest_set_orig(pdu, pdu_len, &c->vmac);
|
||||
ret = bsc_send(dst, pdu, pdu_len);
|
||||
(void)ret;
|
||||
#if DEBUG_ENABLED == 1
|
||||
if (ret != BSC_SC_SUCCESS) {
|
||||
DEBUG_PRINTF(
|
||||
"sending of pdu of %d bytes failed, err = %d\n",
|
||||
pdu_len, ret);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ev == BSC_SOCKET_EVENT_DISCONNECTED) {
|
||||
hub_function_update_status(f, c, ev, reason, reason_desc);
|
||||
if (reason == ERROR_CODE_NODE_DUPLICATE_VMAC) {
|
||||
f->event_func(
|
||||
BSC_HUBF_EVENT_ERROR_DUPLICATED_VMAC,
|
||||
(BSC_HUB_FUNCTION_HANDLE)f, f->user_arg);
|
||||
}
|
||||
} else if (ev == BSC_SOCKET_EVENT_CONNECTED) {
|
||||
hub_function_update_status(f, c, ev, reason, reason_desc);
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("hub_function_socket_event() <<<\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle a hub function context event
|
||||
* @param ctx - pointer to the socket context
|
||||
* @param ev - event
|
||||
*/
|
||||
static void hub_function_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev)
|
||||
{
|
||||
BSC_HUB_FUNCTION *f;
|
||||
bws_dispatch_lock();
|
||||
f = (BSC_HUB_FUNCTION *)ctx->user_arg;
|
||||
if (ev == BSC_CTX_INITIALIZED) {
|
||||
f->state = BSC_HUB_FUNCTION_STATE_STARTED;
|
||||
f->event_func(
|
||||
BSC_HUBF_EVENT_STARTED, (BSC_HUB_FUNCTION_HANDLE)f, f->user_arg);
|
||||
} else if (ev == BSC_CTX_DEINITIALIZED) {
|
||||
f->state = BSC_HUB_FUNCTION_STATE_IDLE;
|
||||
hub_function_free(f);
|
||||
f->event_func(
|
||||
BSC_HUBF_EVENT_STOPPED, (BSC_HUB_FUNCTION_HANDLE)f, f->user_arg);
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start a BACnet hub function
|
||||
* @param ca_cert_chain - pointer to the CA certificate chain
|
||||
* @param ca_cert_chain_size - size of the CA certificate chain
|
||||
* @param cert_chain - pointer to the certificate chain
|
||||
* @param cert_chain_size - size of the certificate chain
|
||||
* @param key - pointer to the private key
|
||||
* @param key_size - size of the private key
|
||||
* @param port - port number
|
||||
* @param iface - pointer to the interface
|
||||
* @param local_uuid - pointer to the local UUID
|
||||
* @param local_vmac - pointer to the local VMAC address
|
||||
* @param max_local_bvlc_len - maximum local BVLC length
|
||||
* @param max_local_npdu_len - maximum local NPDU length
|
||||
* @param connect_timeout_s - connection timeout in seconds
|
||||
* @param heartbeat_timeout_s - heartbeat timeout in seconds
|
||||
* @param disconnect_timeout_s - disconnect timeout in seconds
|
||||
* @param event_func - pointer to the event function
|
||||
* @param user_arg - pointer to the user argument
|
||||
* @param h - pointer to the hub function handle
|
||||
* @return BACnet/SC status
|
||||
*/
|
||||
BSC_SC_RET bsc_hub_function_start(
|
||||
uint8_t *ca_cert_chain,
|
||||
size_t ca_cert_chain_size,
|
||||
uint8_t *cert_chain,
|
||||
size_t cert_chain_size,
|
||||
uint8_t *key,
|
||||
size_t key_size,
|
||||
int port,
|
||||
char *iface,
|
||||
BACNET_SC_UUID *local_uuid,
|
||||
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||
uint16_t max_local_bvlc_len,
|
||||
uint16_t max_local_npdu_len,
|
||||
unsigned int connect_timeout_s,
|
||||
unsigned int heartbeat_timeout_s,
|
||||
unsigned int disconnect_timeout_s,
|
||||
BSC_HUB_EVENT_FUNC event_func,
|
||||
void *user_arg,
|
||||
BSC_HUB_FUNCTION_HANDLE *h)
|
||||
{
|
||||
BSC_SC_RET ret;
|
||||
BSC_HUB_FUNCTION *f;
|
||||
|
||||
DEBUG_PRINTF("bsc_hub_function_start() >>>\n");
|
||||
|
||||
if (!ca_cert_chain || !ca_cert_chain_size || !cert_chain ||
|
||||
!cert_chain_size || !key || !key_size || !local_uuid || !local_vmac ||
|
||||
!max_local_npdu_len || !max_local_bvlc_len || !connect_timeout_s ||
|
||||
!heartbeat_timeout_s || !disconnect_timeout_s || !event_func || !h) {
|
||||
DEBUG_PRINTF("bsc_hub_function_start() <<< ret = BSC_SC_BAD_PARAM\n");
|
||||
return BSC_SC_BAD_PARAM;
|
||||
}
|
||||
|
||||
*h = NULL;
|
||||
|
||||
bws_dispatch_lock();
|
||||
f = hub_function_alloc();
|
||||
|
||||
if (!f) {
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF(
|
||||
"bsc_hub_function_start() <<< ret = BSC_SC_NO_RESOURCES\n");
|
||||
return BSC_SC_NO_RESOURCES;
|
||||
}
|
||||
|
||||
f->user_arg = user_arg;
|
||||
f->event_func = event_func;
|
||||
|
||||
bsc_init_ctx_cfg(
|
||||
BSC_SOCKET_CTX_ACCEPTOR, &f->cfg, BSC_WEBSOCKET_HUB_PROTOCOL, port,
|
||||
iface, ca_cert_chain, ca_cert_chain_size, cert_chain, cert_chain_size,
|
||||
key, key_size, local_uuid, local_vmac, max_local_bvlc_len,
|
||||
max_local_npdu_len, connect_timeout_s, heartbeat_timeout_s,
|
||||
disconnect_timeout_s);
|
||||
|
||||
ret = bsc_init_ctx(
|
||||
&f->ctx, &f->cfg, &bsc_hub_function_ctx_funcs, f->sock,
|
||||
sizeof(f->sock) / sizeof(BSC_SOCKET), f);
|
||||
|
||||
if (ret == BSC_SC_SUCCESS) {
|
||||
f->state = BSC_HUB_FUNCTION_STATE_STARTING;
|
||||
*h = (BSC_HUB_FUNCTION_HANDLE)f;
|
||||
} else {
|
||||
hub_function_free(f);
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_hub_function_start() << ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stop a BACnet hub function
|
||||
* @param h - pointer to the hub function handle
|
||||
*/
|
||||
void bsc_hub_function_stop(BSC_HUB_FUNCTION_HANDLE h)
|
||||
{
|
||||
BSC_HUB_FUNCTION *f = (BSC_HUB_FUNCTION *)h;
|
||||
DEBUG_PRINTF("bsc_hub_function_stop() >>> h = %p\n", h);
|
||||
bws_dispatch_lock();
|
||||
if (f && f->state != BSC_HUB_FUNCTION_STATE_IDLE &&
|
||||
f->state != BSC_HUB_FUNCTION_STATE_STOPPING) {
|
||||
f->state = BSC_HUB_FUNCTION_STATE_STOPPING;
|
||||
bsc_deinit_ctx(&f->ctx);
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_hub_function_stop() <<<\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the hub function is stopped
|
||||
* @param h - pointer to the hub function handle
|
||||
* @return true if the hub function is stopped, false otherwise
|
||||
*/
|
||||
bool bsc_hub_function_stopped(BSC_HUB_FUNCTION_HANDLE h)
|
||||
{
|
||||
BSC_HUB_FUNCTION *f = (BSC_HUB_FUNCTION *)h;
|
||||
bool ret = false;
|
||||
|
||||
DEBUG_PRINTF("bsc_hub_function_stopped() >>> h = %p\n", h);
|
||||
bws_dispatch_lock();
|
||||
if (f && f->state == BSC_HUB_FUNCTION_STATE_IDLE) {
|
||||
ret = true;
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_hub_function_stopped() <<< ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the hub function is started
|
||||
* @param h - pointer to the hub function handle
|
||||
* @return true if the hub function is started, false otherwise
|
||||
*/
|
||||
bool bsc_hub_function_started(BSC_HUB_FUNCTION_HANDLE h)
|
||||
{
|
||||
BSC_HUB_FUNCTION *f = (BSC_HUB_FUNCTION *)h;
|
||||
bool ret = false;
|
||||
|
||||
DEBUG_PRINTF("bsc_hub_function_started() >>> h = %p\n", h);
|
||||
bws_dispatch_lock();
|
||||
if (f && f->state == BSC_HUB_FUNCTION_STATE_STARTED) {
|
||||
ret = true;
|
||||
}
|
||||
bws_dispatch_unlock();
|
||||
DEBUG_PRINTF("bsc_hub_function_started() <<< ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief BACNet secure connect hub function API.
|
||||
* In general, user should not use that API directly,
|
||||
* BACNet/SC datalink API should be used.
|
||||
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||
* @date July 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_DATALINK_BSC_HUB_FUNCTION_H
|
||||
#define BACNET_DATALINK_BSC_HUB_FUNCTION_H
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||
#include "bacnet/basic/object/sc_netport.h"
|
||||
|
||||
typedef void *BSC_HUB_FUNCTION_HANDLE;
|
||||
|
||||
typedef enum {
|
||||
BSC_HUBF_EVENT_STARTED = 1,
|
||||
BSC_HUBF_EVENT_STOPPED = 2,
|
||||
BSC_HUBF_EVENT_ERROR_DUPLICATED_VMAC = 3
|
||||
} BSC_HUB_FUNCTION_EVENT;
|
||||
|
||||
typedef void (*BSC_HUB_EVENT_FUNC)(
|
||||
BSC_HUB_FUNCTION_EVENT ev, BSC_HUB_FUNCTION_HANDLE h, void *user_arg);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_hub_function_start(
|
||||
uint8_t *ca_cert_chain,
|
||||
size_t ca_cert_chain_size,
|
||||
uint8_t *cert_chain,
|
||||
size_t cert_chain_size,
|
||||
uint8_t *key,
|
||||
size_t key_size,
|
||||
int port,
|
||||
char *iface,
|
||||
BACNET_SC_UUID *local_uuid,
|
||||
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||
uint16_t max_local_bvlc_len,
|
||||
uint16_t max_local_npdu_len,
|
||||
unsigned int connect_timeout_s,
|
||||
unsigned int heartbeat_timeout_s,
|
||||
unsigned int disconnect_timeout_s,
|
||||
BSC_HUB_EVENT_FUNC event_func,
|
||||
void *user_arg,
|
||||
BSC_HUB_FUNCTION_HANDLE *h);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_hub_function_stop(BSC_HUB_FUNCTION_HANDLE h);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bsc_hub_function_stopped(BSC_HUB_FUNCTION_HANDLE h);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bsc_hub_function_started(BSC_HUB_FUNCTION_HANDLE h);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief BACNet secure connect node switch function API.
|
||||
* In general, user should not use that API directly,
|
||||
* BACNet/SC datalink API should be used.
|
||||
* @author Kirill Neznamov <kirill\.neznamov@dsr-corporation\.com>
|
||||
* @date October 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_DATALINK_BSC_NODE_SWITCH_H
|
||||
#define BACNET_DATALINK_BSC_NODE_SWITCH_H
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||
#include "bacnet/datalink/bsc/bsc-node.h"
|
||||
|
||||
typedef void *BSC_NODE_SWITCH_HANDLE;
|
||||
|
||||
typedef enum {
|
||||
BSC_NODE_SWITCH_EVENT_STARTED = 1,
|
||||
BSC_NODE_SWITCH_EVENT_STOPPED = 2,
|
||||
BSC_NODE_SWITCH_EVENT_RECEIVED = 3,
|
||||
BSC_NODE_SWITCH_EVENT_DUPLICATED_VMAC = 4,
|
||||
|
||||
/* BSC_NODE_SWITCH_EVENT_CONNECTED event is emitted every */
|
||||
/* time remote peer connects only if bsc_node_switch_connect() */
|
||||
/* was called after start for corresponded mac or url/urls. */
|
||||
/* Events indication are stopped only */
|
||||
/* if node switch was stopped or bsc_node_switch_disconnect() */
|
||||
/* was called. For a connection initiated from a remote peer that */
|
||||
/* event won't be ever emitted. */
|
||||
|
||||
BSC_NODE_SWITCH_EVENT_CONNECTED = 5,
|
||||
|
||||
/* The BSC_NODE_SWITCH_EVENT_DISCONNECTED event is emitted */
|
||||
/* every time remote peer disconnects only if */
|
||||
/* bsc_node_switch_connect() was called after start. */
|
||||
/* If user called bsc_node_switch_disconnect() or stopped */
|
||||
/* node switch, after last event indication next event indications */
|
||||
/* are stopped for corresponded peer with corresponded vmac. */
|
||||
BSC_NODE_SWITCH_EVENT_DISCONNECTED = 6
|
||||
|
||||
} BSC_NODE_SWITCH_EVENT;
|
||||
|
||||
/* dest parameter is actual only for */
|
||||
/* BSC_NODE_SWITCH_EVENT_CONNECTED and BSC_NODE_SWITCH_EVENT_DISCONNECTED */
|
||||
/* events. It is the parameter which was specified in */
|
||||
/* bsc_node_switch_connect() or bsc_node_switch_disconnect() calls. */
|
||||
|
||||
typedef void (*BSC_NODE_SWITCH_EVENT_FUNC)(
|
||||
BSC_NODE_SWITCH_EVENT ev,
|
||||
BSC_NODE_SWITCH_HANDLE h,
|
||||
void *user_arg,
|
||||
BACNET_SC_VMAC_ADDRESS *dest,
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
BVLC_SC_DECODED_MESSAGE *decoded_pdu);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_node_switch_start(
|
||||
uint8_t *ca_cert_chain,
|
||||
size_t ca_cert_chain_size,
|
||||
uint8_t *cert_chain,
|
||||
size_t cert_chain_size,
|
||||
uint8_t *key,
|
||||
size_t key_size,
|
||||
int port,
|
||||
char *iface,
|
||||
BACNET_SC_UUID *local_uuid,
|
||||
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||
uint16_t max_local_bvlc_len,
|
||||
uint16_t max_local_npdu_len,
|
||||
uint16_t connect_timeout_s,
|
||||
uint16_t heartbeat_timeout_s,
|
||||
uint16_t disconnect_timeout_s,
|
||||
uint16_t reconnnect_timeout_s,
|
||||
uint16_t address_resolution_timeout_s,
|
||||
bool direct_connect_accept_enable,
|
||||
bool direct_connect_initiate_enable,
|
||||
BSC_NODE_SWITCH_EVENT_FUNC event_func,
|
||||
void *user_arg,
|
||||
BSC_NODE_SWITCH_HANDLE *h);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_node_switch_stop(BSC_NODE_SWITCH_HANDLE h);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bsc_node_switch_stopped(BSC_NODE_SWITCH_HANDLE h);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bsc_node_switch_started(BSC_NODE_SWITCH_HANDLE h);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_node_switch_connect(
|
||||
BSC_NODE_SWITCH_HANDLE h,
|
||||
BACNET_SC_VMAC_ADDRESS *dest,
|
||||
char **urls,
|
||||
size_t urls_cnt);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bsc_node_switch_connected(
|
||||
BSC_NODE_SWITCH_HANDLE h,
|
||||
BACNET_SC_VMAC_ADDRESS *dest,
|
||||
char **urls,
|
||||
size_t urls_cnt);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_node_switch_disconnect(
|
||||
BSC_NODE_SWITCH_HANDLE h, BACNET_SC_VMAC_ADDRESS *dest);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_node_switch_process_address_resolution(
|
||||
BSC_NODE_SWITCH_HANDLE h, BSC_ADDRESS_RESOLUTION *r);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET
|
||||
bsc_node_switch_send(BSC_NODE_SWITCH_HANDLE h, uint8_t *pdu, size_t pdu_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_node_switch_maintenance_timer(uint16_t seconds);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief BACNet secure connect node API.
|
||||
* In general, user should not use that API directly,
|
||||
* BACNet/SC datalink API should be used.
|
||||
* @author Kirill Neznamov <kirill\.neznamov@dsr-corporation\.com>
|
||||
* @date October 2022
|
||||
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*/
|
||||
#ifndef BACNET_DATALINK_BSC_NODE_H
|
||||
#define BACNET_DATALINK_BSC_NODE_H
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||
#include "bacnet/basic/sys/mstimer.h"
|
||||
#include "bacnet/basic/object/sc_netport.h"
|
||||
|
||||
typedef struct BSC_Node BSC_NODE;
|
||||
|
||||
typedef enum {
|
||||
BSC_NODE_EVENT_STARTED = 1,
|
||||
BSC_NODE_EVENT_STOPPED = 2,
|
||||
BSC_NODE_EVENT_RESTARTED = 3,
|
||||
BSC_NODE_EVENT_RECEIVED_NPDU = 4,
|
||||
BSC_NODE_EVENT_RECEIVED_RESULT = 5,
|
||||
BSC_NODE_EVENT_RECEIVED_ADVERTISIMENT = 6,
|
||||
BSC_NODE_EVENT_DIRECT_CONNECTED = 7,
|
||||
BSC_NODE_EVENT_DIRECT_DISCONNECTED = 8
|
||||
} BSC_NODE_EVENT;
|
||||
|
||||
typedef void (*BSC_NODE_EVENT_FUNC)(
|
||||
BSC_NODE *node,
|
||||
BSC_NODE_EVENT ev,
|
||||
BACNET_SC_VMAC_ADDRESS *dest,
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len);
|
||||
typedef struct {
|
||||
bool used;
|
||||
BACNET_SC_VMAC_ADDRESS vmac;
|
||||
uint8_t utf8_urls[BSC_CONF_NODE_MAX_URIS_NUM_IN_ADDRESS_RESOLUTION_ACK]
|
||||
[BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK + 1];
|
||||
size_t urls_num;
|
||||
struct mstimer fresh_timer;
|
||||
} BSC_ADDRESS_RESOLUTION;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *ca_cert_chain;
|
||||
size_t ca_cert_chain_size;
|
||||
uint8_t *cert_chain;
|
||||
size_t cert_chain_size;
|
||||
uint8_t *key;
|
||||
size_t key_size;
|
||||
BACNET_SC_UUID *local_uuid;
|
||||
BACNET_SC_VMAC_ADDRESS local_vmac;
|
||||
uint16_t max_local_bvlc_len;
|
||||
uint16_t max_local_npdu_len;
|
||||
uint16_t connect_timeout_s;
|
||||
uint16_t heartbeat_timeout_s;
|
||||
uint16_t disconnect_timeout_s;
|
||||
uint16_t reconnnect_timeout_s;
|
||||
uint16_t address_resolution_timeout_s;
|
||||
uint16_t address_resolution_freshness_timeout_s;
|
||||
char *primaryURL;
|
||||
char *failoverURL;
|
||||
uint16_t hub_server_port;
|
||||
uint16_t direct_server_port;
|
||||
char *hub_iface;
|
||||
char *direct_iface;
|
||||
bool direct_connect_accept_enable;
|
||||
bool direct_connect_initiate_enable;
|
||||
bool hub_function_enabled;
|
||||
char *direct_connection_accept_uris; /* URIs joined ' 'space */
|
||||
size_t direct_connection_accept_uris_len;
|
||||
BSC_NODE_EVENT_FUNC event_func;
|
||||
} BSC_NODE_CONF;
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_node_init(BSC_NODE_CONF *conf, BSC_NODE **node);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_node_deinit(BSC_NODE *node);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_node_start(BSC_NODE *node);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_node_stop(BSC_NODE *node);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_node_send(BSC_NODE *node, uint8_t *pdu, size_t pdu_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_ADDRESS_RESOLUTION *
|
||||
bsc_node_get_address_resolution(void *node, BACNET_SC_VMAC_ADDRESS *vmac);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET
|
||||
bsc_node_send_address_resolution(void *node, BACNET_SC_VMAC_ADDRESS *dest);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET
|
||||
bsc_node_hub_connector_send(void *user_arg, uint8_t *pdu, size_t pdu_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_node_connect_direct(
|
||||
BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_node_disconnect_direct(BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *dest);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_HUB_CONNECTOR_STATE
|
||||
bsc_node_hub_connector_state(BSC_NODE *node);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_HUB_CONNECTION_STATUS *
|
||||
bsc_node_hub_connector_status(BSC_NODE *node, bool primary);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bsc_node_direct_connection_established(
|
||||
BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_node_maintenance_timer(uint16_t seconds);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *
|
||||
bsc_node_hub_function_status(BSC_NODE *node, size_t *cnt);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_DIRECT_CONNECTION_STATUS *
|
||||
bsc_node_direct_connection_status(BSC_NODE *node, size_t *cnt);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_node_store_failed_request_info(
|
||||
BSC_NODE *node,
|
||||
BACNET_HOST_N_PORT_DATA *peer,
|
||||
BACNET_SC_VMAC_ADDRESS *vmac,
|
||||
BACNET_SC_UUID *uuid,
|
||||
BACNET_ERROR_CODE error,
|
||||
const char *error_desc);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_FAILED_CONNECTION_REQUEST *
|
||||
bsc_node_failed_requests_status(BSC_NODE *node, size_t *cnt);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_DIRECT_CONNECTION_STATUS *bsc_node_find_direct_status_for_vmac(
|
||||
BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *vmac);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *
|
||||
bsc_node_find_hub_status_for_vmac(BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *vmac);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief BACNet secure connect main include header.
|
||||
* @author Kirill Neznamov <kirill\.neznamov@dsr-corporation\.com>
|
||||
* @date August 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_DATALINK_BSC_RETCODES_H
|
||||
#define BACNET_DATALINK_BSC_RETCODES_H
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
|
||||
typedef enum {
|
||||
BSC_SC_SUCCESS = 0,
|
||||
BSC_SC_NO_RESOURCES = 1,
|
||||
BSC_SC_BAD_PARAM = 2,
|
||||
BSC_SC_INVALID_OPERATION = 3
|
||||
} BSC_SC_RET;
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,308 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief BACNet secure connect socket API.
|
||||
* In general, user should not use that API directly,
|
||||
* BACNet/SC datalink API should be used.
|
||||
* @author Kirill Neznamov <kirill\.neznamov@dsr-corporation\.com>
|
||||
* @date December 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_DATALINK_BSC_SOCKET_H
|
||||
#define BACNET_DATALINK_BSC_SOCKET_H
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/datalink/bsc/websocket.h"
|
||||
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||
#include "bacnet/basic/sys/mstimer.h"
|
||||
#include "bacnet/basic/object/sc_netport.h"
|
||||
|
||||
#define BSC_RX_BUFFER_SIZE BSC_CONF_SOCK_RX_BUFFER_SIZE
|
||||
#define BSC_TX_BUFFER_SIZE BSC_CONF_SOCK_TX_BUFFER_SIZE
|
||||
|
||||
#define BSC_SOCKET_CTX_NUM \
|
||||
(BSC_CONF_NODES_NUM * \
|
||||
(BSC_CONF_HUB_CONNECTORS_NUM + 2 * BSC_CONF_NODE_SWITCHES_NUM + \
|
||||
BSC_CONF_HUB_FUNCTIONS_NUM))
|
||||
|
||||
struct BSC_Socket;
|
||||
typedef struct BSC_Socket BSC_SOCKET;
|
||||
|
||||
struct BSC_SocketContext;
|
||||
typedef struct BSC_SocketContext BSC_SOCKET_CTX;
|
||||
|
||||
struct BSC_SocketContextFuncs;
|
||||
typedef struct BSC_SocketContextFuncs BSC_SOCKET_CTX_FUNCS;
|
||||
|
||||
struct BSC_ContextCFG;
|
||||
typedef struct BSC_ContextCFG BSC_CONTEXT_CFG;
|
||||
|
||||
typedef enum {
|
||||
BSC_SOCKET_CTX_INITIATOR = 1,
|
||||
BSC_SOCKET_CTX_ACCEPTOR = 2
|
||||
} BSC_SOCKET_CTX_TYPE;
|
||||
|
||||
typedef enum {
|
||||
BSC_SOCKET_EVENT_CONNECTED = 0,
|
||||
BSC_SOCKET_EVENT_DISCONNECTED = 1,
|
||||
BSC_SOCKET_EVENT_RECEIVED = 2
|
||||
} BSC_SOCKET_EVENT;
|
||||
|
||||
typedef enum {
|
||||
BSC_CTX_INITIALIZED = 0,
|
||||
BSC_CTX_DEINITIALIZED = 1
|
||||
} BSC_CTX_EVENT;
|
||||
|
||||
typedef enum {
|
||||
BSC_CTX_STATE_IDLE = 0,
|
||||
BSC_CTX_STATE_INITIALIZING = 1,
|
||||
BSC_CTX_STATE_INITIALIZED = 2,
|
||||
BSC_CTX_STATE_DEINITIALIZING = 3
|
||||
} BSC_CTX_STATE;
|
||||
|
||||
typedef enum {
|
||||
BSC_SOCK_STATE_IDLE = 0,
|
||||
BSC_SOCK_STATE_AWAITING_WEBSOCKET = 1,
|
||||
BSC_SOCK_STATE_AWAITING_REQUEST = 2,
|
||||
BSC_SOCK_STATE_AWAITING_ACCEPT = 3,
|
||||
BSC_SOCK_STATE_CONNECTED = 4,
|
||||
BSC_SOCK_STATE_DISCONNECTING = 5,
|
||||
BSC_SOCK_STATE_ERROR = 6,
|
||||
BSC_SOCK_STATE_ERROR_FLUSH_TX = 7
|
||||
} BSC_SOCKET_STATE;
|
||||
|
||||
struct BSC_Socket {
|
||||
BSC_SOCKET_CTX *ctx;
|
||||
BSC_WEBSOCKET_HANDLE wh;
|
||||
BSC_SOCKET_STATE state;
|
||||
BACNET_ERROR_CODE reason;
|
||||
struct mstimer t;
|
||||
struct mstimer heartbeat;
|
||||
BACNET_SC_VMAC_ADDRESS vmac; /* VMAC address of the requesting node. */
|
||||
BACNET_SC_UUID uuid;
|
||||
|
||||
/* Regarding max_bvlc_len and max_npdu_len: */
|
||||
/* These are the datalink limits and are passed up the stack to let */
|
||||
/* the application layer know one of the several numbers that go into
|
||||
* computing */
|
||||
/* how big an NPDU/APDU can be. */
|
||||
|
||||
uint16_t max_bvlc_len; /* remote peer max bvlc len */
|
||||
uint16_t max_npdu_len; /* remote peer max npdu len */
|
||||
|
||||
uint16_t expected_connect_accept_message_id;
|
||||
uint16_t expected_disconnect_message_id;
|
||||
uint16_t expected_heartbeat_message_id;
|
||||
|
||||
uint8_t tx_buf[BSC_TX_BUFFER_SIZE];
|
||||
size_t tx_buf_size;
|
||||
};
|
||||
|
||||
struct BSC_ContextCFG {
|
||||
BSC_SOCKET_CTX_TYPE type;
|
||||
BSC_WEBSOCKET_PROTOCOL proto;
|
||||
uint16_t port;
|
||||
char *iface;
|
||||
uint8_t *ca_cert_chain;
|
||||
size_t ca_cert_chain_size;
|
||||
uint8_t *cert_chain;
|
||||
size_t cert_chain_size;
|
||||
uint8_t *priv_key;
|
||||
size_t priv_key_size;
|
||||
BACNET_SC_VMAC_ADDRESS local_vmac;
|
||||
BACNET_SC_UUID local_uuid;
|
||||
uint16_t max_bvlc_len; /* local peer max bvlc len */
|
||||
uint16_t max_ndpu_len; /* local peer max npdu len */
|
||||
|
||||
/* According AB.6.2 BACnet/SC Connection Establishment and Termination */
|
||||
/* recommended default value for establishing of connection 10 seconds */
|
||||
uint16_t connect_timeout_s;
|
||||
uint16_t disconnect_timeout_s;
|
||||
|
||||
/* According 12.56.Y10 SC_Heartbeat_Timeout */
|
||||
/* (http://www.bacnet.org/Addenda/Add-135-2020cc.pdf) the recommended
|
||||
* default */
|
||||
/* value is 300 seconds. */
|
||||
uint16_t heartbeat_timeout_s;
|
||||
};
|
||||
|
||||
struct BSC_SocketContextFuncs {
|
||||
BSC_SOCKET *(*find_connection_for_vmac)(
|
||||
BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg);
|
||||
BSC_SOCKET *(*find_connection_for_uuid)(
|
||||
BACNET_SC_UUID *uuid, void *user_arg);
|
||||
/* We always reserve BSC_PRE bytes before BVLC message header */
|
||||
/* to avoid copying of packet payload during manipulation with */
|
||||
/* origin and dest addresses (e.g. adding them to received PDU) */
|
||||
/* That's why pdu pointer has always reserved BSC_PRE bytes behind */
|
||||
/* The params disconnect_reason and disconnect_reason_desc are meanfull */
|
||||
/* only for disconnect events, e.g. when ev == BSC_SOCKET_EVENT_DISCONNECTED
|
||||
*/
|
||||
|
||||
void (*socket_event)(
|
||||
BSC_SOCKET *c,
|
||||
BSC_SOCKET_EVENT ev,
|
||||
BACNET_ERROR_CODE disconnect_reason,
|
||||
const char *disconnect_reason_desc,
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
BVLC_SC_DECODED_MESSAGE *decoded_pdu);
|
||||
|
||||
void (*context_event)(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev);
|
||||
void (*failed_request)(
|
||||
BSC_SOCKET_CTX *ctx,
|
||||
BSC_SOCKET *c,
|
||||
BACNET_SC_VMAC_ADDRESS *vmac,
|
||||
BACNET_SC_UUID *uuid,
|
||||
BACNET_ERROR_CODE error,
|
||||
const char *error_desc);
|
||||
};
|
||||
|
||||
struct BSC_SocketContext {
|
||||
BSC_CTX_STATE state;
|
||||
BSC_WEBSOCKET_SRV_HANDLE sh;
|
||||
BSC_SOCKET *sock;
|
||||
size_t sock_num;
|
||||
BSC_SOCKET_CTX_FUNCS *funcs;
|
||||
BSC_CONTEXT_CFG *cfg;
|
||||
bool deinit_in_progress;
|
||||
void *user_arg;
|
||||
};
|
||||
|
||||
/* max_local_bvlc_len - The maximum BVLC message size int bytes that can be */
|
||||
/* received and processed by BSC/SC datalink. */
|
||||
/* max_local_ndpu_len - The maximum NPDU message size in bytes hat can be */
|
||||
/* handled by BSC/SC datalink. */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_init_ctx_cfg(
|
||||
BSC_SOCKET_CTX_TYPE type,
|
||||
BSC_CONTEXT_CFG *cfg,
|
||||
BSC_WEBSOCKET_PROTOCOL proto,
|
||||
uint16_t port,
|
||||
char *iface,
|
||||
uint8_t *ca_cert_chain,
|
||||
size_t ca_cert_chain_size,
|
||||
uint8_t *cert_chain,
|
||||
size_t cert_chain_size,
|
||||
uint8_t *key,
|
||||
size_t key_size,
|
||||
BACNET_SC_UUID *local_uuid,
|
||||
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||
uint16_t max_local_bvlc_len,
|
||||
uint16_t max_local_ndpu_len,
|
||||
unsigned int connect_timeout_s,
|
||||
unsigned int heartbeat_timeout_s,
|
||||
unsigned int disconnect_timeout_s);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_init_ctx(
|
||||
BSC_SOCKET_CTX *ctx,
|
||||
BSC_CONTEXT_CFG *cfg,
|
||||
BSC_SOCKET_CTX_FUNCS *funcs,
|
||||
BSC_SOCKET *sockets,
|
||||
size_t sockets_num,
|
||||
void *user_arg);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_deinit_ctx(BSC_SOCKET_CTX *ctx);
|
||||
|
||||
/**
|
||||
* @brief bsc_connect() function starts connect operation for a
|
||||
* specified BACNet socket. The function call be called only
|
||||
* for initiator context otherwise BSC_SC_INVALID_OPERATION
|
||||
* error is returned. As a result if bsc_connect() was
|
||||
* succeeded for given param c, that leads to emitting of
|
||||
* BSC_SOCKET_EVENT_CONNECTED or BSC_SOCKET_EVENT_DISCONNECTED
|
||||
* events depending on the result of connect operation.
|
||||
* If connect operation is failed, BSC_SOCKET_EVENT_DISCONNECTED
|
||||
* event is emitted and user can determine the reason why it
|
||||
* happened using disconnect_reason and disconnect_reason_desc
|
||||
* parameters in callback function.
|
||||
* If connect operation succeeded, BSC_SOCKET_EVENT_CONNECTED
|
||||
* event is emitted.
|
||||
*
|
||||
* @param ctx - socket context.
|
||||
* @param c - BACNet socket descriptor .
|
||||
* @param url - url to connect to. For example: wss://legrand.com:8080.
|
||||
*
|
||||
* @return error code from BSC_SC_RET enum.
|
||||
* The following error codes can be returned:
|
||||
* BSC_SC_BAD_PARAM - In a case if some input parameter is
|
||||
* incorrect.
|
||||
* BSC_SC_INVALID_OPERATION - if socket is not in opened state,
|
||||
or disconnect operation is in progress using
|
||||
bsc_disconnect() or bsc_deinit_ctx().
|
||||
* BSC_SC_SUCCESS - operation has succeeded.
|
||||
* BSC_SC_NO_RESOURCES - there are not resources (memory, etc.. )
|
||||
* to send data
|
||||
*/
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_connect(BSC_SOCKET_CTX *ctx, BSC_SOCKET *c, char *url);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_disconnect(BSC_SOCKET *c);
|
||||
|
||||
/**
|
||||
* @brief bsc_send() function schedules transmitting of pdu to
|
||||
* another BACNet socket. The function may be used only
|
||||
* when the socket is in a connected state
|
||||
* otherwise BSC_SC_INVALID_OPERATION error is returned.
|
||||
*
|
||||
* @param c - BACNet socket descriptor initialized by bsc_accept() or
|
||||
* bsc_connect() calls.
|
||||
* @param pdu - pointer to a data to send.
|
||||
* @param pdu_len - size in bytes of data to send.
|
||||
*
|
||||
* @return error code from BSC_SC_RET enum.
|
||||
* The following error codes can be returned:
|
||||
* BSC_SC_BAD_PARAM - In a case if some input parameter is
|
||||
* incorrect.
|
||||
* BSC_SC_INVALID_OPERATION - if socket is not in opened state,
|
||||
or disconnect operation is in progress using
|
||||
bsc_disconnect() or bsc_deinit_ctx().
|
||||
* BSC_SC_SUCCESS - operation has succeeded.
|
||||
* BSC_SC_NO_RESOURCES - there are not resources (memory, etc.. )
|
||||
* to send data
|
||||
*/
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BSC_SC_RET bsc_send(BSC_SOCKET *c, uint8_t *pdu, size_t pdu_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint16_t bsc_get_next_message_id(void);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bsc_socket_maintenance_timer(uint16_t seconds);
|
||||
|
||||
/**
|
||||
* @brief bsc_socket_get_peer_addr() function gets information
|
||||
* about remote peer address only for socket with acceptor cotext.
|
||||
*
|
||||
* @param c - BACNet socket descriptor initialized by bsc_accept() call.
|
||||
* @param data - pointer to a struct holding address information.
|
||||
* @param pdu_len - size in bytes of data to send.
|
||||
*
|
||||
* @return false if socket is not acceptor or bad parameter was passed or
|
||||
* if getting of address information failed.
|
||||
* true if function succeeds.
|
||||
*/
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bsc_socket_get_peer_addr(BSC_SOCKET *c, BACNET_HOST_N_PORT_DATA *data);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint8_t *bsc_socket_get_global_buf(void);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bsc_socket_get_global_buf_size(void);
|
||||
#endif
|
||||
@@ -0,0 +1,343 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief module for common function for BACnet/SC implementation
|
||||
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||
* @date Jule 2022
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*/
|
||||
#include "bacnet/datalink/bsc/bsc-util.h"
|
||||
#include "bacnet/basic/object/bacfile.h"
|
||||
#include "bacnet/basic/object/netport.h"
|
||||
#include "bacnet/basic/object/sc_netport.h"
|
||||
#include "bacnet/basic/object/bacfile.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define PRINTF debug_aprintf
|
||||
#define PRINTF_ERR debug_perror
|
||||
|
||||
/**
|
||||
* @brief Map websocket return code to BACnet/SC return code
|
||||
* @param ret - websocket return code
|
||||
* @return BACnet/SC return code
|
||||
*/
|
||||
BSC_SC_RET bsc_map_websocket_retcode(BSC_WEBSOCKET_RET ret)
|
||||
{
|
||||
switch (ret) {
|
||||
case BSC_WEBSOCKET_SUCCESS:
|
||||
return BSC_SC_SUCCESS;
|
||||
case BSC_WEBSOCKET_NO_RESOURCES:
|
||||
return BSC_SC_NO_RESOURCES;
|
||||
case BSC_WEBSOCKET_BAD_PARAM:
|
||||
return BSC_SC_BAD_PARAM;
|
||||
case BSC_WEBSOCKET_INVALID_OPERATION:
|
||||
default:
|
||||
return BSC_SC_INVALID_OPERATION;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy BACnet Secure Connect VMAC address
|
||||
* @param dst - destination VMAC address
|
||||
* @param src - source VMAC address
|
||||
*/
|
||||
void bsc_copy_vmac(BACNET_SC_VMAC_ADDRESS *dst, BACNET_SC_VMAC_ADDRESS *src)
|
||||
{
|
||||
memcpy(dst->address, src->address, sizeof(src->address));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy the BACnet Secure Connect UUID
|
||||
* @param dst - destination UUID
|
||||
* @param src - source UUID
|
||||
*/
|
||||
void bsc_copy_uuid(BACNET_SC_UUID *dst, BACNET_SC_UUID *src)
|
||||
{
|
||||
memcpy(dst->uuid, src->uuid, sizeof(src->uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert BACnet Secure Connect VMAC address to string
|
||||
* @param vmac - VMAC address
|
||||
* @return string representation of VMAC address
|
||||
*/
|
||||
char *bsc_vmac_to_string(BACNET_SC_VMAC_ADDRESS *vmac)
|
||||
{
|
||||
static char buf[128];
|
||||
sprintf(
|
||||
buf, "%02x%02x%02x%02x%02x%02x", vmac->address[0], vmac->address[1],
|
||||
vmac->address[2], vmac->address[3], vmac->address[4], vmac->address[5]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert BACnet Secure Connect UUID to string
|
||||
* @param uuid - UUID
|
||||
* @return string representation of UUID
|
||||
*/
|
||||
char *bsc_uuid_to_string(BACNET_SC_UUID *uuid)
|
||||
{
|
||||
static char buf[128];
|
||||
sprintf(
|
||||
buf,
|
||||
"%02x%02x%02x%02x-%02x%02x%02x%02x-%02x%02x%02x%02x-%02x%02x%02x%02x",
|
||||
uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3],
|
||||
uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
|
||||
uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11],
|
||||
uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate random BACnet Secure Connect VMAC address
|
||||
* @param p - pointer to the VMAC address
|
||||
*/
|
||||
void bsc_generate_random_vmac(BACNET_SC_VMAC_ADDRESS *p)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BVLC_SC_VMAC_SIZE; i++) {
|
||||
p->address[i] = rand() % 255;
|
||||
if (i == 0) {
|
||||
/* According H.7.3 EUI-48 and Random-48 VMAC Address:
|
||||
The Random-48 VMAC is a 6-octet VMAC address in which the least
|
||||
significant 4 bits (Bit 3 to Bit 0) in the first octet shall be
|
||||
B'0010' (X'2'), and all other 44 bits are randomly selected to be
|
||||
0 or 1. */
|
||||
p->address[i] = (p->address[i] & 0xF0) | 0x02;
|
||||
}
|
||||
}
|
||||
debug_printf_hex(
|
||||
0, p->address, BVLC_SC_VMAC_SIZE, "bsc_generate_random_vmac");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate random BACnet Secure Connect UUID
|
||||
* @param p - pointer to the UUID
|
||||
*/
|
||||
void bsc_generate_random_uuid(BACNET_SC_UUID *p)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BVLC_SC_UUID_SIZE; i++) {
|
||||
p->uuid[i] = rand() % 255;
|
||||
}
|
||||
debug_printf_hex(0, p->uuid, BVLC_SC_UUID_SIZE, "bsc_generate_random_uuid");
|
||||
}
|
||||
|
||||
/*
|
||||
* bsc_node_load_cert_bacfile loads one credentional file from bacfile object
|
||||
* Note: the function adds null-terminated byte to loaded file
|
||||
* (certificate, certificate key).
|
||||
* The MbedTLS PEM parser requires data to be null-terminated.
|
||||
*/
|
||||
#ifdef CONFIG_MBEDTLS
|
||||
#define ZERO_BYTE 1
|
||||
#else
|
||||
#define ZERO_BYTE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Load certificate from BACnet file object
|
||||
* @param file_instance - file instance
|
||||
* @param pbuf - pointer to the buffer
|
||||
* @param psize - pointer to the size
|
||||
* @return true if successful, else false
|
||||
*/
|
||||
static bool bsc_node_load_cert_bacfile(
|
||||
uint32_t file_instance, uint8_t **pbuf, size_t *psize)
|
||||
{
|
||||
uint32_t file_length;
|
||||
uint8_t *buf;
|
||||
|
||||
*psize = bacfile_file_size(file_instance) + ZERO_BYTE;
|
||||
if (*psize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buf = calloc(1, *psize);
|
||||
if (buf == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file_length =
|
||||
bacfile_read(file_instance, buf, (uint32_t)(*psize - ZERO_BYTE));
|
||||
#ifdef CONFIG_MBEDTLS
|
||||
buf[*psize - 1] = 0;
|
||||
#endif
|
||||
if (file_length == 0) {
|
||||
PRINTF_ERR("Can't read %s file\n", bacfile_pathname(file_instance));
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
*pbuf = buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fill BACnet/SC node configuration from network port object
|
||||
* @param bsc_conf - pointer to the BACnet/SC node configuration
|
||||
* @param event_func - event function
|
||||
* @return true if successful, else false
|
||||
*/
|
||||
bool bsc_node_conf_fill_from_netport(
|
||||
BSC_NODE_CONF *bsc_conf, BSC_NODE_EVENT_FUNC event_func)
|
||||
{
|
||||
uint32_t instance;
|
||||
uint32_t file_instance;
|
||||
|
||||
instance = Network_Port_Index_To_Instance(0);
|
||||
bsc_conf->ca_cert_chain = NULL;
|
||||
bsc_conf->cert_chain = NULL;
|
||||
bsc_conf->key = NULL;
|
||||
|
||||
file_instance = Network_Port_Issuer_Certificate_File(instance, 0);
|
||||
if (!bsc_node_load_cert_bacfile(
|
||||
file_instance, &bsc_conf->ca_cert_chain,
|
||||
&bsc_conf->ca_cert_chain_size)) {
|
||||
bsc_node_conf_cleanup(bsc_conf);
|
||||
return false;
|
||||
}
|
||||
|
||||
file_instance = Network_Port_Operational_Certificate_File(instance);
|
||||
if (!bsc_node_load_cert_bacfile(
|
||||
file_instance, &bsc_conf->cert_chain, &bsc_conf->cert_chain_size)) {
|
||||
bsc_node_conf_cleanup(bsc_conf);
|
||||
return false;
|
||||
}
|
||||
|
||||
file_instance = Network_Port_Certificate_Key_File(instance);
|
||||
bsc_conf->key_size = bacfile_file_size(file_instance) + 1;
|
||||
if (!bsc_node_load_cert_bacfile(
|
||||
file_instance, &bsc_conf->key, &bsc_conf->key_size)) {
|
||||
bsc_node_conf_cleanup(bsc_conf);
|
||||
return false;
|
||||
}
|
||||
bsc_conf->local_uuid =
|
||||
(BACNET_SC_UUID *)Network_Port_SC_Local_UUID(instance);
|
||||
Network_Port_MAC_Address_Value(
|
||||
instance, bsc_conf->local_vmac.address,
|
||||
sizeof(bsc_conf->local_vmac.address));
|
||||
bsc_conf->max_local_bvlc_len =
|
||||
(uint16_t)Network_Port_Max_BVLC_Length_Accepted(instance);
|
||||
bsc_conf->max_local_npdu_len =
|
||||
(uint16_t)Network_Port_Max_NPDU_Length_Accepted(instance);
|
||||
bsc_conf->connect_timeout_s =
|
||||
(uint16_t)Network_Port_SC_Connect_Wait_Timeout(instance);
|
||||
bsc_conf->heartbeat_timeout_s =
|
||||
(uint16_t)Network_Port_SC_Heartbeat_Timeout(instance);
|
||||
bsc_conf->disconnect_timeout_s =
|
||||
(uint16_t)Network_Port_SC_Disconnect_Wait_Timeout(instance);
|
||||
bsc_conf->reconnnect_timeout_s =
|
||||
(uint16_t)Network_Port_SC_Maximum_Reconnect_Time(instance);
|
||||
bsc_conf->address_resolution_timeout_s = bsc_conf->connect_timeout_s;
|
||||
bsc_conf->address_resolution_freshness_timeout_s =
|
||||
bsc_conf->connect_timeout_s;
|
||||
bsc_conf->primaryURL =
|
||||
(char *)Network_Port_SC_Primary_Hub_URI_char(instance);
|
||||
bsc_conf->failoverURL =
|
||||
(char *)Network_Port_SC_Failover_Hub_URI_char(instance);
|
||||
#if BSC_CONF_HUB_CONNECTORS_NUM != 0
|
||||
bsc_conf->direct_connect_initiate_enable =
|
||||
Network_Port_SC_Direct_Connect_Initiate_Enable(instance);
|
||||
bsc_conf->direct_connect_accept_enable =
|
||||
Network_Port_SC_Direct_Connect_Accept_Enable(instance);
|
||||
Network_Port_SC_Direct_Connect_Binding_get(
|
||||
instance, &bsc_conf->direct_server_port, &bsc_conf->direct_iface);
|
||||
#endif
|
||||
#if BSC_CONF_HUB_FUNCTIONS_NUM != 0
|
||||
Network_Port_SC_Hub_Function_Binding_get(
|
||||
instance, &bsc_conf->hub_server_port, &bsc_conf->hub_iface);
|
||||
bsc_conf->hub_function_enabled =
|
||||
Network_Port_SC_Hub_Function_Enable(instance);
|
||||
#endif
|
||||
|
||||
#if BSC_CONF_HUB_CONNECTORS_NUM != 0
|
||||
bsc_conf->direct_connection_accept_uris =
|
||||
Network_Port_SC_Direct_Connect_Accept_URIs_char(instance);
|
||||
bsc_conf->direct_connection_accept_uris_len =
|
||||
strlen(bsc_conf->direct_connection_accept_uris);
|
||||
#endif
|
||||
bsc_conf->event_func = event_func;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cleanup BACnet/SC node configuration
|
||||
* @param bsc_conf - pointer to the BACnet/SC node configuration
|
||||
*/
|
||||
void bsc_node_conf_cleanup(BSC_NODE_CONF *bsc_conf)
|
||||
{
|
||||
bsc_conf->ca_cert_chain_size = 0;
|
||||
if (bsc_conf->ca_cert_chain) {
|
||||
free(bsc_conf->ca_cert_chain);
|
||||
}
|
||||
bsc_conf->cert_chain_size = 0;
|
||||
if (bsc_conf->cert_chain) {
|
||||
free(bsc_conf->cert_chain);
|
||||
}
|
||||
bsc_conf->key_size = 0;
|
||||
if (bsc_conf->key) {
|
||||
free(bsc_conf->key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy string
|
||||
* @param dst - destination string
|
||||
* @param src - source string
|
||||
* @param dst_len - destination string length
|
||||
*/
|
||||
void bsc_copy_str(char *dst, const char *src, size_t dst_len)
|
||||
{
|
||||
size_t len;
|
||||
if (dst_len > 0) {
|
||||
len = strlen(src) >= dst_len ? dst_len - 1 : strlen(src);
|
||||
memcpy(dst, src, len);
|
||||
dst[len] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set timestamp
|
||||
* @param timestamp - pointer to the timestamp
|
||||
*/
|
||||
void bsc_set_timestamp(BACNET_DATE_TIME *timestamp)
|
||||
{
|
||||
int16_t utc_offset_minutes;
|
||||
bool dst_active;
|
||||
datetime_local(
|
||||
×tamp->date, ×tamp->time, &utc_offset_minutes, &dst_active);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if BACnet/SC certificate files exist
|
||||
* @return true if all files exist, else false
|
||||
*/
|
||||
bool bsc_cert_files_check(void)
|
||||
{
|
||||
uint32_t instance;
|
||||
uint32_t file_instance;
|
||||
|
||||
instance = Network_Port_Index_To_Instance(0);
|
||||
|
||||
file_instance = Network_Port_Issuer_Certificate_File(instance, 0);
|
||||
if (bacfile_file_size(file_instance) == 0) {
|
||||
PRINTF_ERR("CA certificate file not exist\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
file_instance = Network_Port_Operational_Certificate_File(instance);
|
||||
if (bacfile_file_size(file_instance) == 0) {
|
||||
PRINTF_ERR("Certificate file not exist\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
file_instance = Network_Port_Certificate_Key_File(instance);
|
||||
if (bacfile_file_size(file_instance) == 0) {
|
||||
PRINTF_ERR("Certificate key file not exist\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief module for common function for BACnet/SC implementation
|
||||
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||
* @date Jule 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_DATALINK_BSC_UTIL_H
|
||||
#define BACNET_DATALINK_BSC_UTIL_H
|
||||
#include <stdbool.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/basic/sys/mstimer.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/datalink/bsc/bsc-node.h"
|
||||
#include "bacnet/datalink/bsc/bsc-retcodes.h"
|
||||
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||
#include "bacnet/datalink/bsc/websocket.h"
|
||||
#include "bacnet/datetime.h"
|
||||
|
||||
BSC_SC_RET bsc_map_websocket_retcode(BSC_WEBSOCKET_RET ret);
|
||||
|
||||
void bsc_copy_vmac(BACNET_SC_VMAC_ADDRESS *dst, BACNET_SC_VMAC_ADDRESS *src);
|
||||
void bsc_copy_uuid(BACNET_SC_UUID *dst, BACNET_SC_UUID *src);
|
||||
char *bsc_vmac_to_string(BACNET_SC_VMAC_ADDRESS *vmac);
|
||||
char *bsc_uuid_to_string(BACNET_SC_UUID *uuid);
|
||||
void bsc_generate_random_vmac(BACNET_SC_VMAC_ADDRESS *p);
|
||||
void bsc_generate_random_uuid(BACNET_SC_UUID *p);
|
||||
bool bsc_node_conf_fill_from_netport(
|
||||
BSC_NODE_CONF *bsc_conf, BSC_NODE_EVENT_FUNC event_func);
|
||||
void bsc_node_conf_cleanup(BSC_NODE_CONF *bsc_conf);
|
||||
void bsc_copy_str(char *dst, const char *src, size_t dst_len);
|
||||
void bsc_set_timestamp(BACNET_DATE_TIME *timestamp);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,425 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief API for encoding/decoding of BACnet/SC BVLC messages
|
||||
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||
* @date May 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_DATALINK_BSC_BVLC_SC_H
|
||||
#define BACNET_DATALINK_BSC_BVLC_SC_H
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
#ifndef BVLC_SC_NPDU_SIZE_CONF
|
||||
#define BVLC_SC_NPDU_SIZE 1440
|
||||
#else
|
||||
#define BVLC_SC_NPDU_SIZE BVLC_SC_NPDU_SIZE_CONF
|
||||
#endif
|
||||
|
||||
#define BVLC_SC_NPDU_MAX_SIZE \
|
||||
61327 /* Table 6-1. NPDU Lengths of BACnet Data Link Layers */
|
||||
#define BVLC_SC_VMAC_SIZE 6
|
||||
#define BVLC_SC_UUID_SIZE 16
|
||||
#define BSC_PRE (2 * BVLC_SC_VMAC_SIZE)
|
||||
|
||||
#if !defined(USER_DEFINED_BVLC_SC_HEADER_OPTION_MAX)
|
||||
#define BVLC_SC_HEADER_OPTION_MAX \
|
||||
4 /* though BACNet standard does not limit number of option headers \
|
||||
the implementation defines max value */
|
||||
#else
|
||||
#define BVLC_SC_HEADER_OPTION_MAX USER_DEFINED_BVLC_SC_HEADER_OPTION_MAX
|
||||
#endif
|
||||
|
||||
#if BVLC_SC_NPDU_SIZE > BVLC_SC_NPDU_MAX_SIZE
|
||||
#error \
|
||||
"Maximum NPDU Length on BACNet/SC Data Link must be <= BVLC_SC_NPDU_MAX_SIZE"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* BACnet/SC BVLC Messages (functions) (AB.2 BACnet/SC Virtual Link Layer
|
||||
* Messages)
|
||||
*/
|
||||
|
||||
typedef enum BVLC_SC_Message_Type {
|
||||
BVLC_SC_RESULT = 0x00,
|
||||
BVLC_SC_ENCAPSULATED_NPDU = 0x01,
|
||||
BVLC_SC_ADDRESS_RESOLUTION = 0x02,
|
||||
BVLC_SC_ADDRESS_RESOLUTION_ACK = 0x03,
|
||||
BVLC_SC_ADVERTISIMENT = 0x04,
|
||||
BVLC_SC_ADVERTISIMENT_SOLICITATION = 0x05,
|
||||
BVLC_SC_CONNECT_REQUEST = 0x06,
|
||||
BVLC_SC_CONNECT_ACCEPT = 0x07,
|
||||
BVLC_SC_DISCONNECT_REQUEST = 0x08,
|
||||
BVLC_SC_DISCONNECT_ACK = 0x09,
|
||||
BVLC_SC_HEARTBEAT_REQUEST = 0x0a,
|
||||
BVLC_SC_HEARTBEAT_ACK = 0x0b,
|
||||
BVLC_SC_PROPRIETARY_MESSAGE = 0x0c
|
||||
} BVLC_SC_MESSAGE_TYPE;
|
||||
|
||||
/*
|
||||
* AB.2.2 Control Flags
|
||||
*/
|
||||
|
||||
#define BVLC_SC_CONTROL_DATA_OPTIONS (1 << 0)
|
||||
#define BVLC_SC_CONTROL_DEST_OPTIONS (1 << 1)
|
||||
#define BVLC_SC_CONTROL_DEST_VADDR (1 << 2)
|
||||
#define BVLC_SC_CONTROL_ORIG_VADDR (1 << 3)
|
||||
|
||||
/*
|
||||
* AB.2.3 Header Options
|
||||
*/
|
||||
|
||||
#define BVLC_SC_HEADER_DATA (1 << 5)
|
||||
#define BVLC_SC_HEADER_MUST_UNDERSTAND (1 << 6)
|
||||
#define BVLC_SC_HEADER_MORE (1 << 7)
|
||||
#define BVLC_SC_HEADER_OPTION_TYPE_MASK (0x1F)
|
||||
|
||||
/**
|
||||
* BACnet SC VMAC Address
|
||||
*
|
||||
* B.1.5.2 VMAC Addressing of Nodes
|
||||
* For the BVLC message exchange, BACnet/SC nodes are identified by their
|
||||
* 6-octet virtual MAC address as defined in Clause H.7.3.
|
||||
* For broadcast BVLC messages that need to reach all nodes of the BACnet/SC
|
||||
* network, the destination VMAC address shall be the non-EUI-48 value
|
||||
* X'FFFFFFFFFFFF', referred to as the Local Broadcast VMAC address.
|
||||
* The reserved EUI-48 value X'000000000000' is not used by this data link
|
||||
* and therefore can be used internally to indicate that a VMAC is unknown
|
||||
* or uninitialized.
|
||||
* @{
|
||||
*/
|
||||
|
||||
typedef struct BACnet_SC_VMAC_Address {
|
||||
uint8_t address[BVLC_SC_VMAC_SIZE];
|
||||
} BACNET_SC_VMAC_ADDRESS;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* BACnet SC UUID
|
||||
* AB.1.5.3 Device UUID
|
||||
* Every BACnet device that supports one or more BACnet/SC network ports shall
|
||||
* have a Universally Unique ID (UUID) as defined in RFC 4122. This UUID
|
||||
* identifies the device regardless of its current VMAC address or device
|
||||
* instance number and is referred to as the device UUID.
|
||||
* This device UUID shall be generated before first deployment of the device in
|
||||
* an installation, shall be persistently stored across device restarts, and
|
||||
* shall not change over the entire lifetime of a device.
|
||||
* If a device is replaced in an installation, the new device is not required
|
||||
* to reuse the UUID of the replaced device. For BACnet/SC, it is assumed
|
||||
* that existing connections to the device being replaced are all terminated
|
||||
* before the new device comes into operation.
|
||||
* @{
|
||||
*/
|
||||
|
||||
typedef struct BACnet_SC_Uuid {
|
||||
uint8_t uuid[BVLC_SC_UUID_SIZE];
|
||||
} BACNET_SC_UUID;
|
||||
/** @} */
|
||||
|
||||
/*
|
||||
* AB.2.3.1 Secure Path Header Option
|
||||
*/
|
||||
typedef enum BVLC_SC_Option_Type {
|
||||
BVLC_SC_OPTION_TYPE_SECURE_PATH = 1,
|
||||
BVLC_SC_OPTION_TYPE_PROPRIETARY = 31
|
||||
} BVLC_SC_OPTION_TYPE;
|
||||
|
||||
typedef enum BVLC_SC_Direct_Connection_Support {
|
||||
BVLC_SC_DIRECT_CONNECTION_ACCEPT_UNSUPPORTED = 0,
|
||||
BVLC_SC_DIRECT_CONNECTION_ACCEPT_SUPPORTED = 1,
|
||||
BVLC_SC_DIRECT_CONNECTION_SUPPORT_MAX = 1
|
||||
} BVLC_SC_DIRECT_CONNECTION_SUPPORT;
|
||||
|
||||
typedef struct BVLC_SC_Decoded_Hdr {
|
||||
uint8_t bvlc_function;
|
||||
uint16_t message_id;
|
||||
BACNET_SC_VMAC_ADDRESS *origin;
|
||||
BACNET_SC_VMAC_ADDRESS *dest;
|
||||
uint8_t *dest_options; /* pointer to packed dest options list in message */
|
||||
size_t dest_options_len;
|
||||
size_t dest_options_num; /* number of filled items in dest_options */
|
||||
uint8_t *data_options; /* pointer to packed data options list in message */
|
||||
size_t data_options_len;
|
||||
size_t data_options_num; /* number of filled items in data_options */
|
||||
uint8_t *payload; /* packed payload, points to data in message */
|
||||
size_t payload_len;
|
||||
} BVLC_SC_DECODED_HDR;
|
||||
|
||||
typedef struct BVLC_SC_Decoded_Result {
|
||||
uint8_t bvlc_function;
|
||||
uint8_t result;
|
||||
uint8_t error_header_marker;
|
||||
uint16_t error_class;
|
||||
uint16_t error_code;
|
||||
uint8_t *utf8_details_string; /* NOTE!: this is utf 8 string without
|
||||
trailing zero */
|
||||
size_t utf8_details_string_len;
|
||||
} BVLC_SC_DECODED_RESULT;
|
||||
|
||||
typedef struct BVLC_SC_Decoded_Address_Resolution_Ack {
|
||||
uint8_t *utf8_websocket_uri_string; /* NOTE!: this is utf 8 string without
|
||||
trailing zero */
|
||||
size_t utf8_websocket_uri_string_len;
|
||||
} BVLC_SC_DECODED_ADDRESS_RESOLUTION_ACK;
|
||||
|
||||
typedef struct BVLC_SC_Decoded_Ecapsulated_NPDU {
|
||||
uint8_t *npdu;
|
||||
size_t npdu_len;
|
||||
} BVLC_SC_DECODED_ENCAPSULATED_NPDU;
|
||||
|
||||
typedef struct BVLC_SC_Decoded_Advertisiment {
|
||||
BACNET_SC_HUB_CONNECTOR_STATE hub_status;
|
||||
BVLC_SC_DIRECT_CONNECTION_SUPPORT support;
|
||||
uint16_t max_bvlc_len;
|
||||
uint16_t max_npdu_len;
|
||||
} BVLC_SC_DECODED_ADVERTISIMENT;
|
||||
|
||||
typedef struct BVLC_SC_Decoded_Connect_Request {
|
||||
BACNET_SC_VMAC_ADDRESS *vmac;
|
||||
BACNET_SC_UUID *uuid;
|
||||
uint16_t max_bvlc_len;
|
||||
uint16_t max_npdu_len;
|
||||
} BVLC_SC_DECODED_CONNECT_REQUEST;
|
||||
|
||||
typedef struct BVLC_SC_Decoded_Connect_Accept {
|
||||
BACNET_SC_VMAC_ADDRESS *vmac;
|
||||
BACNET_SC_UUID *uuid;
|
||||
uint16_t max_bvlc_len;
|
||||
uint16_t max_npdu_len;
|
||||
} BVLC_SC_DECODED_CONNECT_ACCEPT;
|
||||
|
||||
typedef struct BVLC_SC_Decoded_Proprietary {
|
||||
uint16_t vendor_id;
|
||||
uint8_t function;
|
||||
uint8_t *data;
|
||||
size_t data_len;
|
||||
} BVLC_SC_DECODED_PROPRIETARY;
|
||||
|
||||
typedef union BVLC_SC_Decoded_Data {
|
||||
BVLC_SC_DECODED_RESULT result;
|
||||
BVLC_SC_DECODED_ENCAPSULATED_NPDU encapsulated_npdu;
|
||||
BVLC_SC_DECODED_ADDRESS_RESOLUTION_ACK address_resolution_ack;
|
||||
BVLC_SC_DECODED_ADVERTISIMENT advertisiment;
|
||||
BVLC_SC_DECODED_CONNECT_REQUEST connect_request;
|
||||
BVLC_SC_DECODED_CONNECT_ACCEPT connect_accept;
|
||||
BVLC_SC_DECODED_PROPRIETARY proprietary;
|
||||
} BVLC_SC_DECODED_DATA;
|
||||
|
||||
typedef struct BVLC_SC_Decoded_Hdr_Proprietary_Option {
|
||||
uint16_t vendor_id;
|
||||
uint8_t option_type;
|
||||
uint8_t *data;
|
||||
size_t data_len;
|
||||
} BVLC_SC_DECODED_HDR_PROPRIETARY_OPTION;
|
||||
|
||||
typedef union BVLC_SC_Decoded_Specific_Option_Data {
|
||||
BVLC_SC_DECODED_HDR_PROPRIETARY_OPTION proprietary;
|
||||
} BVLC_SC_DECODED_SPECIFIC_OPTION_DATA;
|
||||
|
||||
typedef struct BVLC_SC_Decoded_Hdr_Option {
|
||||
uint8_t packed_header_marker;
|
||||
BVLC_SC_OPTION_TYPE type;
|
||||
bool must_understand;
|
||||
BVLC_SC_DECODED_SPECIFIC_OPTION_DATA specific;
|
||||
} BVLC_SC_DECODED_HDR_OPTION;
|
||||
|
||||
typedef struct BVLC_SC_Decoded_Message {
|
||||
BVLC_SC_DECODED_HDR hdr;
|
||||
BVLC_SC_DECODED_HDR_OPTION data_options[BVLC_SC_HEADER_OPTION_MAX];
|
||||
BVLC_SC_DECODED_HDR_OPTION dest_options[BVLC_SC_HEADER_OPTION_MAX];
|
||||
BVLC_SC_DECODED_DATA payload;
|
||||
} BVLC_SC_DECODED_MESSAGE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_add_option_to_destination_options(
|
||||
uint8_t *out_pdu,
|
||||
size_t out_pdu_size,
|
||||
uint8_t *pdu,
|
||||
size_t pdu_size,
|
||||
uint8_t *sc_option,
|
||||
size_t sc_option_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_add_option_to_data_options(
|
||||
uint8_t *out_pdu,
|
||||
size_t out_pdu_size,
|
||||
uint8_t *pdu,
|
||||
size_t pdu_size,
|
||||
uint8_t *sc_option,
|
||||
size_t sc_option_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_proprietary_option(
|
||||
uint8_t *pdu,
|
||||
size_t pdu_size,
|
||||
bool must_understand,
|
||||
uint16_t vendor_id,
|
||||
uint8_t proprietary_option_type,
|
||||
uint8_t *proprietary_data,
|
||||
size_t proprietary_data_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_secure_path_option(
|
||||
uint8_t *pdu, size_t pdu_size, bool must_understand);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_result(
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
uint16_t message_id,
|
||||
BACNET_SC_VMAC_ADDRESS *origin,
|
||||
BACNET_SC_VMAC_ADDRESS *dest,
|
||||
uint8_t bvlc_function,
|
||||
uint8_t result_code,
|
||||
uint8_t *error_header_marker,
|
||||
uint16_t *error_class,
|
||||
uint16_t *error_code,
|
||||
uint8_t *utf8_details_string);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_encapsulated_npdu(
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
uint16_t message_id,
|
||||
BACNET_SC_VMAC_ADDRESS *origin,
|
||||
BACNET_SC_VMAC_ADDRESS *dest,
|
||||
uint8_t *npdu,
|
||||
size_t npdu_size);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_address_resolution(
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
uint16_t message_id,
|
||||
BACNET_SC_VMAC_ADDRESS *origin,
|
||||
BACNET_SC_VMAC_ADDRESS *dest);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_address_resolution_ack(
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
uint16_t message_id,
|
||||
BACNET_SC_VMAC_ADDRESS *origin,
|
||||
BACNET_SC_VMAC_ADDRESS *dest,
|
||||
uint8_t *web_socket_uris,
|
||||
size_t web_socket_uris_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_advertisiment(
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
uint16_t message_id,
|
||||
BACNET_SC_VMAC_ADDRESS *origin,
|
||||
BACNET_SC_VMAC_ADDRESS *dest,
|
||||
BACNET_SC_HUB_CONNECTOR_STATE hub_status,
|
||||
BVLC_SC_DIRECT_CONNECTION_SUPPORT support,
|
||||
uint16_t max_bvlc_len,
|
||||
uint16_t max_npdu_size);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_advertisiment_solicitation(
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
uint16_t message_id,
|
||||
BACNET_SC_VMAC_ADDRESS *origin,
|
||||
BACNET_SC_VMAC_ADDRESS *dest);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_connect_request(
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
uint16_t message_id,
|
||||
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||
BACNET_SC_UUID *local_uuid,
|
||||
uint16_t max_bvlc_len,
|
||||
uint16_t max_npdu_size);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_connect_accept(
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
uint16_t message_id,
|
||||
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
||||
BACNET_SC_UUID *local_uuid,
|
||||
uint16_t max_bvlc_len,
|
||||
uint16_t max_npdu_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_disconnect_request(
|
||||
uint8_t *pdu, size_t pdu_len, uint16_t message_id);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_disconnect_ack(
|
||||
uint8_t *pdu, size_t pdu_len, uint16_t message_id);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_heartbeat_request(
|
||||
uint8_t *out_buf, size_t out_buf_len, uint16_t message_id);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_heartbeat_ack(
|
||||
uint8_t *out_buf, size_t out_buf_len, uint16_t message_id);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_encode_proprietary_message(
|
||||
uint8_t *pdu,
|
||||
size_t pdu_len,
|
||||
uint16_t message_id,
|
||||
BACNET_SC_VMAC_ADDRESS *origin,
|
||||
BACNET_SC_VMAC_ADDRESS *dest,
|
||||
uint16_t vendor_id,
|
||||
uint8_t proprietary_function,
|
||||
uint8_t *proprietary_data,
|
||||
size_t proprietary_data_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bvlc_sc_decode_message(
|
||||
uint8_t *buf,
|
||||
size_t buf_len,
|
||||
BVLC_SC_DECODED_MESSAGE *message,
|
||||
BACNET_ERROR_CODE *error,
|
||||
BACNET_ERROR_CLASS *class,
|
||||
const char **err_desc);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bvlc_sc_remove_dest_set_orig(
|
||||
uint8_t *pdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *orig);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t
|
||||
bvlc_sc_set_orig(uint8_t **ppdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *orig);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bvlc_sc_is_vmac_broadcast(BACNET_SC_VMAC_ADDRESS *vmac);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bvlc_sc_need_send_bvlc_result(BVLC_SC_DECODED_MESSAGE *dm);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bvlc_sc_pdu_has_dest_broadcast(uint8_t *pdu, size_t pdu_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bvlc_sc_pdu_has_no_dest(uint8_t *pdu, size_t pdu_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bvlc_sc_pdu_get_dest(
|
||||
uint8_t *pdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *vmac);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bvlc_sc_remove_orig_and_dest(uint8_t **ppdu, size_t pdu_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,450 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Client/Server thread-safe websocket interface API.
|
||||
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
||||
* @date May 2022
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_DATALINK_BSC_WEBSOCKET_H
|
||||
#define BACNET_DATALINK_BSC_WEBSOCKET_H
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||
|
||||
/**
|
||||
* Maximum number of sockets that can be opened on client's side.
|
||||
* @{
|
||||
*/
|
||||
#ifndef BSC_CONF_CLIENT_CONNECTIONS_NUM
|
||||
#define BSC_CLIENT_WEBSOCKETS_MAX_NUM 4
|
||||
#else
|
||||
#define BSC_CLIENT_WEBSOCKETS_MAX_NUM BSC_CONF_CLIENT_CONNECTIONS_NUM
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Maximum number of server instances. Value 10 means that
|
||||
* 10 hub servers and 10 direct servers can be started.
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef BSC_CONF_WEBSOCKET_SERVERS_NUM
|
||||
#define BSC_CONF_WEBSOCKET_SERVERS_NUM 1
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Maximum number of sockets supported for hub websocket server
|
||||
* @{
|
||||
*/
|
||||
#ifndef BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM
|
||||
#define BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM 4
|
||||
#else
|
||||
#define BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM \
|
||||
BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Initial size of websocket buffer used on receive. It can be increased
|
||||
* dynamically depending on received packet size.
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef BSC_CONF_WEBSOCKET_RX_BUFFER_LEN
|
||||
#define BSC_WEBSOCKET_RX_BUFFER_LEN 512
|
||||
#else
|
||||
#define BSC_WEBSOCKET_RX_BUFFER_LEN BSC_CONF_WEBSOCKET_RX_BUFFER_LEN
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Maximum number of sockets supported for direct websocket server
|
||||
* @{
|
||||
*/
|
||||
#ifndef BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM
|
||||
#define BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM 4
|
||||
#else
|
||||
#define BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM \
|
||||
BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM
|
||||
#endif
|
||||
|
||||
#ifndef BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN
|
||||
#define BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN 128
|
||||
#else
|
||||
#define BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN \
|
||||
BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN
|
||||
#endif
|
||||
|
||||
#define BSC_WSURL_MAX_LEN BSC_CONF_WSURL_MAX_LEN
|
||||
|
||||
typedef int BSC_WEBSOCKET_HANDLE;
|
||||
typedef void *BSC_WEBSOCKET_SRV_HANDLE;
|
||||
#define BSC_WEBSOCKET_INVALID_HANDLE (-1)
|
||||
|
||||
/* Websockets protocol defined in BACnet/SC \S AB.7.1. */
|
||||
#define BSC_WEBSOCKET_HUB_PROTOCOL_STR "hub.bsc.bacnet.org"
|
||||
#define BSC_WEBSOCKET_DIRECT_PROTOCOL_STR "dc.bsc.bacnet.org"
|
||||
|
||||
typedef enum {
|
||||
BSC_WEBSOCKET_HUB_PROTOCOL = 0,
|
||||
BSC_WEBSOCKET_DIRECT_PROTOCOL = 1,
|
||||
BSC_WEBSOCKET_PROTOCOLS_AMOUNT = 2 /* must be always last */
|
||||
} BSC_WEBSOCKET_PROTOCOL;
|
||||
|
||||
typedef enum {
|
||||
BSC_WEBSOCKET_SUCCESS = 0,
|
||||
BSC_WEBSOCKET_NO_RESOURCES = 1,
|
||||
BSC_WEBSOCKET_BAD_PARAM = 2,
|
||||
BSC_WEBSOCKET_INVALID_OPERATION = 3
|
||||
} BSC_WEBSOCKET_RET;
|
||||
|
||||
typedef enum {
|
||||
BSC_WEBSOCKET_CONNECTED = 0,
|
||||
BSC_WEBSOCKET_DISCONNECTED = 1,
|
||||
BSC_WEBSOCKET_RECEIVED = 2,
|
||||
BSC_WEBSOCKET_SENDABLE = 3,
|
||||
BSC_WEBSOCKET_SERVER_STARTED = 4,
|
||||
BSC_WEBSOCKET_SERVER_STOPPED = 5
|
||||
} BSC_WEBSOCKET_EVENT;
|
||||
|
||||
/*
|
||||
values of ws_reason and ws_reason_desc parameters are actual
|
||||
only for BSC_WEBSOCKET_DISCONNECTED event.
|
||||
*/
|
||||
|
||||
typedef void (*BSC_WEBSOCKET_CLI_DISPATCH)(
|
||||
BSC_WEBSOCKET_HANDLE h,
|
||||
BSC_WEBSOCKET_EVENT ev,
|
||||
BACNET_ERROR_CODE ws_reason,
|
||||
char *ws_reason_desc,
|
||||
uint8_t *buf,
|
||||
size_t bufsize,
|
||||
void *dispatch_func_user_param);
|
||||
|
||||
typedef void (*BSC_WEBSOCKET_SRV_DISPATCH)(
|
||||
BSC_WEBSOCKET_SRV_HANDLE sh,
|
||||
BSC_WEBSOCKET_HANDLE h,
|
||||
BSC_WEBSOCKET_EVENT ev,
|
||||
BACNET_ERROR_CODE ws_reason,
|
||||
char *ws_reason_desc,
|
||||
uint8_t *buf,
|
||||
size_t bufsize,
|
||||
void *dispatch_func_user_param);
|
||||
|
||||
/**
|
||||
* @brief Asynchronous bws_cli_сonnect() function starts establishing
|
||||
* of a new connection to a websocket server specified by url parameter.
|
||||
* Result of completion of operation is call of dispatch_func() with
|
||||
* BSC_WEBSOCKET_CONNECTED in a case if connection established successfully or
|
||||
* BSC_WEBSOCKET_DISCONNECTED if connection attempt failed.
|
||||
*
|
||||
* @param type - type of BACNet/SC connection, check
|
||||
* BSC_WEBSOCKET_CONNECTION_TYPE enum. According BACNet standard
|
||||
* different type of connections require different websocket protocols.
|
||||
* @param url - BACNet/SC server URL. For example: wss://legrand.com:8080.
|
||||
* @param ca_cert - pointer to certificate authority (CA) cert in PEM or DER
|
||||
* format.
|
||||
* @param ca_cert_size - size in bytes of CA cert.
|
||||
* @param cert - pointer to client certificate in PEM or DER format.
|
||||
* @param cert_size - size in bytes of client certificate.
|
||||
* @param key - pointer to client private key in PEM or DER format.
|
||||
* @param key_size - size of private key in bytes of of client certificate.
|
||||
* @param timeout_s - timeout for socket operations (connect, etc..) in seconds.
|
||||
* Must be > 0.
|
||||
* @param dispatch_func - pointer to dispatch callback function to handle
|
||||
* events from websocket specified by *out_handle.
|
||||
* @param dispatch_func_user_param - parameter which is passed into
|
||||
dispatch_func call.
|
||||
* @param out_handle - pointer to a websocket handle.
|
||||
*
|
||||
* @return error code from BSC_WEBSOCKET_RET enum.
|
||||
* The following error codes can be returned:
|
||||
* BSC_WEBSOCKET_BAD_PARAM - In a case if some input parameter is
|
||||
* incorrect.
|
||||
* BSC_WEBSOCKET_NO_RESOURCES - if a user has already opened
|
||||
* more sockets than the limit defined by BSC_CLIENT_WEBSOCKETS_MAX_NUM,
|
||||
* or if some mem allocation has failed or some allocation of system
|
||||
* resources like mutex, thread, etc .., failed.
|
||||
* BSC_WEBSOCKET_SUCCESS - connect operation was successfully started.
|
||||
*/
|
||||
|
||||
BSC_WEBSOCKET_RET bws_cli_connect(
|
||||
BSC_WEBSOCKET_PROTOCOL proto,
|
||||
char *url,
|
||||
uint8_t *ca_cert,
|
||||
size_t ca_cert_size,
|
||||
uint8_t *cert,
|
||||
size_t cert_size,
|
||||
uint8_t *key,
|
||||
size_t key_size,
|
||||
size_t timeout_s,
|
||||
BSC_WEBSOCKET_CLI_DISPATCH dispatch_func,
|
||||
void *dispatch_func_user_param,
|
||||
BSC_WEBSOCKET_HANDLE *out_handle);
|
||||
|
||||
/**
|
||||
* @brief Asynchronous bws_cli_disconnnect() function starts process of
|
||||
* disconnection for specified websocket handle. When the process completes,
|
||||
* dispatch_func() with event BSC_WEBSOCKET_DISCONNECTED is called.
|
||||
* connection to some websocket server.
|
||||
*
|
||||
* @param h - websocket handle.
|
||||
*
|
||||
*/
|
||||
|
||||
void bws_cli_disconnect(BSC_WEBSOCKET_HANDLE h);
|
||||
|
||||
/**
|
||||
* @brief Non-blocked bws_cli_send() function signals to the websocket
|
||||
* specified by websocket handle h that some data is needed to be sent.
|
||||
* When websocket becomes sendable, dispatch_func() is called with
|
||||
* event BSC_WEBSOCKET_SENDABLE and data can be sent from dispatch_func()
|
||||
* call using bws_cli_dispatch_send() call.
|
||||
*
|
||||
* @param h - websocket handle.
|
||||
*
|
||||
*/
|
||||
|
||||
void bws_cli_send(BSC_WEBSOCKET_HANDLE h);
|
||||
|
||||
/**
|
||||
* @brief bws_cli_dispatch_send() function sends data to a websocket server
|
||||
* in a case if websocket handle is sendable (e.g. ready to send data).
|
||||
* In as case if data was not sent for some reasons thic could result
|
||||
* dispatch_func() cal with event BSC_WEBSOCKET_DISCONNECTED
|
||||
* @param h - websocket handle.
|
||||
* @param payload - pointer to a data to send. It is assumed that
|
||||
* BSC_CONF_TX_PRE bytes are available before payload.
|
||||
* So for example you need this kind of code to use
|
||||
* bws_srv_dispatch_send with a 128-byte payload:
|
||||
*
|
||||
* char buf[BSC_CONF_TX_PRE + 128];
|
||||
*
|
||||
* // fill your part of the buffer... for example here
|
||||
* // it's all zeros
|
||||
*
|
||||
* memset(&buf[BSC_CONF_TX_PRE], 0, 128);
|
||||
* bws_cli_dispatch_send(sh, h, &buf[BSC_CONF_TX_PRE], 128);
|
||||
*
|
||||
* @param payload_size - size in bytes of data to send.
|
||||
*
|
||||
* @return error code from BSC_WEBSOCKET_RET enum.
|
||||
* The following error codes can be returned:
|
||||
* BSC_WEBSOCKET_BAD_PARAM - In a case if some input parameter is
|
||||
* incorrect.
|
||||
* BSC_WEBSOCKET_NO_RESOURCES - if some mem allocation has failed o
|
||||
* some allocation of system resources like mutex, thread,
|
||||
* etc .., has failed.
|
||||
* BSC_WEBSOCKET_INVALID_OPERATION - if the function was called not from
|
||||
* dispatch_func() callback context or websocket is not in connected
|
||||
* state.
|
||||
* BSC_WEBSOCKET_SUCCESS - data is sent successfully.
|
||||
*/
|
||||
|
||||
BSC_WEBSOCKET_RET bws_cli_dispatch_send(
|
||||
BSC_WEBSOCKET_HANDLE h, uint8_t *payload, size_t payload_size);
|
||||
|
||||
/**
|
||||
* @brief Asynchronous bws_srv_start() function triggers process of
|
||||
* starting of a websocket server on a specified port for specified
|
||||
* BACNet websocket protocol. At present time peer can have only 2
|
||||
* instances of server: onerelates to BSC_WEBSOCKET_HUB_PROTOCOL and
|
||||
* the other to BSC_WEBSOCKET_HUB_PROTOCOL. When process completes,
|
||||
* dispatch_func() is called with BSC_WEBSOCKET_SERVER_STARTED
|
||||
* event.
|
||||
*
|
||||
* @param proto - type of BACNet websocket protocol defined in
|
||||
* BSC_WEBSOCKET_PROTOCOL enum.
|
||||
* @param port - port number.
|
||||
* @param iface - name of interface to bind to. If the parameter is NULL
|
||||
* that means that websocket server binds to all interfaces.
|
||||
* @param ca_cert - pointer to certificate authority (CA) cert in PEM or DER
|
||||
* format.
|
||||
* @param ca_cert_size - size in bytes of CA cert.
|
||||
* @param cert - pointer to server certificate in PEM or DER format.
|
||||
* @param cert_size - size in bytes of server certificate.
|
||||
* @param key - pointer to server private key in PEM or DER format.
|
||||
* @param key_size - size of private key in bytes of of client certificate.
|
||||
* @param timeout_s - timeout for socket operations (connect, etc..) in seconds.
|
||||
* Must be > 0.
|
||||
* @param dispatch_func - pointer to dispatch callback function to handle
|
||||
* events from a websocket which is corresponded to
|
||||
* server specified by proto param.
|
||||
* @param dispatch_func_user_param - parameter which is passed into
|
||||
* dispatch_func call.
|
||||
* @param sh - pointer to receive websocket server handle
|
||||
*
|
||||
* @return error code from BSC_WEBSOCKET_RET enum.
|
||||
* The following error codes can be returned:
|
||||
* BSC_WEBSOCKET_BAD_PARAM - In a case if some input parameter is
|
||||
* incorrect.
|
||||
* BSC_WEBSOCKET_NO_RESOURCES - if a user has already opened
|
||||
* more sockets than the limit defined to corresponded protocol
|
||||
* (BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM or
|
||||
* BSC_CLIENT_WEBSOCKETS_MAX_NUM), or if some mem allocation
|
||||
* has failed or some allocation of system resources like
|
||||
* mutex, thread, condition variable etc .., failed.
|
||||
* BSC_WEBSOCKET_SUCCESS - the start operation is in progress.
|
||||
* BSC_WEBSOCKET_INVALID_OPERATION - operation is not started because
|
||||
* server in a process of shutdown of server is already started.
|
||||
*/
|
||||
|
||||
BSC_WEBSOCKET_RET bws_srv_start(
|
||||
BSC_WEBSOCKET_PROTOCOL proto,
|
||||
int port,
|
||||
char *iface,
|
||||
uint8_t *ca_cert,
|
||||
size_t ca_cert_size,
|
||||
uint8_t *cert,
|
||||
size_t cert_size,
|
||||
uint8_t *key,
|
||||
size_t key_size,
|
||||
size_t timeout_s,
|
||||
BSC_WEBSOCKET_SRV_DISPATCH dispatch_func,
|
||||
void *dispatch_func_user_param,
|
||||
BSC_WEBSOCKET_SRV_HANDLE *sh);
|
||||
|
||||
/**
|
||||
* @brief Asynchronous bws_srv_stop() function starts process of a shutdowns
|
||||
* of a websocket server specified by proto param.
|
||||
* opened websocket connections are closed.
|
||||
*
|
||||
* @param sh - websocket server handle
|
||||
*
|
||||
* @return error code from BSC_WEBSOCKET_RET enum.
|
||||
* The following error codes can be returned:
|
||||
* BSC_WEBSOCKET_SUCCESS - the operation is started or server was
|
||||
* already stopped before. BSC_WEBSOCKET_INVALID_OPERATION - if server was not
|
||||
* started or server shutdown is already in progress.
|
||||
*/
|
||||
|
||||
BSC_WEBSOCKET_RET bws_srv_stop(BSC_WEBSOCKET_SRV_HANDLE sh);
|
||||
|
||||
/**
|
||||
* @brief Asynchronous bws_srv_disconnnect() function starts process of
|
||||
* disconnection for specified websocket handle h for specified server type
|
||||
* by proto parameter. When the process completes, dispatch_func() with event
|
||||
* BSC_WEBSOCKET_DISCONNECTED is called.
|
||||
*
|
||||
* @param sh - websocket server handle.
|
||||
* @param h - websocket handle.
|
||||
*
|
||||
*/
|
||||
|
||||
void bws_srv_disconnect(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h);
|
||||
|
||||
/**
|
||||
* @brief Asynchronous bws_srv_send() function signals to a websocket
|
||||
* specified by handle h for specified server type by proto param that
|
||||
* some data is needed to be sent.
|
||||
*
|
||||
* When websocket becomes sendable, dispatch_func() is called with
|
||||
* event BSC_WEBSOCKET_SENDABLE and data can be sent from dispatch_func()
|
||||
* call using bws_srv_dispatch_send() call.
|
||||
*
|
||||
* @param sh - websocket server handle.
|
||||
* @param h - websocket handle.
|
||||
*
|
||||
*/
|
||||
|
||||
void bws_srv_send(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h);
|
||||
|
||||
/**
|
||||
* @brief bws_srv_dispatch_send() function sends data to a websocket server
|
||||
* in a case if websocket handle is sendable (e.g. ready to send data).
|
||||
* In as case if data was not sent for some reasons thic could result
|
||||
* dispatch_func() call with event BSC_WEBSOCKET_DISCONNECTED
|
||||
*
|
||||
* @param sh - websocket server handle.
|
||||
* @param h - websocket handle.
|
||||
* @param payload - pointer to a data to send. It is assumed that
|
||||
* BSC_CONF_TX_PRE bytes are available before payload.
|
||||
* So for example you need this kind of code to use
|
||||
* bws_srv_dispatch_send with a 128-byte payload:
|
||||
*
|
||||
* char buf[BSC_CONF_TX_PRE + 128];
|
||||
*
|
||||
* // fill your part of the buffer... for example here
|
||||
* // it's all zeros
|
||||
*
|
||||
* memset(&buf[BSC_CONF_TX_PRE], 0, 128);
|
||||
* bws_srv_dispatch_send(sh, h, &buf[BSC_CONF_TX_PRE], 128);
|
||||
*
|
||||
* @param payload_size - size in bytes of data to send.
|
||||
*
|
||||
* @return error code from BSC_WEBSOCKET_RET enum.
|
||||
* The following error codes can be returned:
|
||||
* BSC_WEBSOCKET_BAD_PARAM - In a case if some input parameter is
|
||||
* incorrect.
|
||||
* BSC_WEBSOCKET_NO_RESOURCES - if some mem allocation has failed o
|
||||
* some allocation of system resources like mutex, thread,
|
||||
* etc .., has failed.
|
||||
* BSC_WEBSOCKET_INVALID_OPERATION - if the function was called not from
|
||||
* dispatch_func() callback context or websocket is not in connected
|
||||
* state or server in a process of a shutdown or server is not started.
|
||||
* BSC_WEBSOCKET_SUCCESS - data is sent successfully.
|
||||
*/
|
||||
|
||||
BSC_WEBSOCKET_RET bws_srv_dispatch_send(
|
||||
BSC_WEBSOCKET_SRV_HANDLE sh,
|
||||
BSC_WEBSOCKET_HANDLE h,
|
||||
uint8_t *payload,
|
||||
size_t payload_size);
|
||||
|
||||
/**
|
||||
* @brief bws_dispatch_lock() function acquires internal websocket
|
||||
* global mutex. As a result dispatch_func() callback for all
|
||||
* websocket server and client instances won't be called until user
|
||||
* calls bws_dispatch_unlock().
|
||||
*/
|
||||
#ifndef BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED
|
||||
#define BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED 0
|
||||
#endif
|
||||
|
||||
#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1)
|
||||
void bws_dispatch_lock(void);
|
||||
#else
|
||||
extern void bws_dispatch_lock_dbg(char *f, int line);
|
||||
#define bws_dispatch_lock() bws_dispatch_lock_dbg(__FILE__, __LINE__)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief bws_dispatch_unlock() function releases internal websocket
|
||||
* global mutex.
|
||||
*/
|
||||
|
||||
#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1)
|
||||
void bws_dispatch_unlock(void);
|
||||
#else
|
||||
extern void bws_dispatch_unlock_dbg(char *f, int line);
|
||||
#define bws_dispatch_unlock() bws_dispatch_unlock_dbg(__FILE__, __LINE__)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief bws_srv_get_peer_ip_addr() gets ipv4 or ipv6 address as ANSI string
|
||||
* and port of remote peer.
|
||||
*
|
||||
* @param sh - websocket server handle.
|
||||
* @param h - websocket handle.
|
||||
* @param ip_str - buffer to store null terminated string of ip address.
|
||||
* @param ip_str_len - size of ip_str buffer
|
||||
* @param port- pointer to store port of a remote node.
|
||||
*
|
||||
* @return true if function succeeded otherwise returns false
|
||||
* if peer's address information can't be retrieved from
|
||||
* underlying websocket library.
|
||||
*/
|
||||
|
||||
bool bws_srv_get_peer_ip_addr(
|
||||
BSC_WEBSOCKET_SRV_HANDLE sh,
|
||||
BSC_WEBSOCKET_HANDLE h,
|
||||
uint8_t *ip_str,
|
||||
size_t ip_str_len,
|
||||
uint16_t *port);
|
||||
#endif
|
||||
@@ -29,6 +29,9 @@
|
||||
#if defined(BACDL_MSTP)
|
||||
#include "bacnet/datalink/dlmstp.h"
|
||||
#endif
|
||||
#if defined(BACDL_BSC)
|
||||
#include "bacnet/datalink/bsc/bsc-datalink.h"
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h> /* for strcasecmp() */
|
||||
#endif
|
||||
@@ -39,7 +42,8 @@ static enum {
|
||||
DATALINK_ETHERNET,
|
||||
DATALINK_BIP,
|
||||
DATALINK_BIP6,
|
||||
DATALINK_MSTP
|
||||
DATALINK_MSTP,
|
||||
DATALINK_BSC
|
||||
} Datalink_Transport;
|
||||
|
||||
void datalink_set(char *datalink_string)
|
||||
@@ -72,6 +76,11 @@ void datalink_set(char *datalink_string)
|
||||
Datalink_Transport = DATALINK_MSTP;
|
||||
}
|
||||
#endif
|
||||
#if defined(BACDL_BSC)
|
||||
else if (strcasecmp("bsc", datalink_string) == 0) {
|
||||
Datalink_Transport = DATALINK_BSC;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool datalink_init(char *ifname)
|
||||
@@ -106,6 +115,11 @@ bool datalink_init(char *ifname)
|
||||
case DATALINK_MSTP:
|
||||
status = dlmstp_init(ifname);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACDL_BSC)
|
||||
case DATALINK_BSC:
|
||||
status = bsc_init(ifname);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@@ -150,6 +164,11 @@ int datalink_send_pdu(
|
||||
case DATALINK_MSTP:
|
||||
bytes = dlmstp_send_pdu(dest, npdu_data, pdu, pdu_len);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACDL_BSC)
|
||||
case DATALINK_BSC:
|
||||
bytes = bsc_send_pdu(dest, npdu_data, pdu, pdu_len);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@@ -190,6 +209,11 @@ uint16_t datalink_receive(
|
||||
case DATALINK_MSTP:
|
||||
bytes = dlmstp_receive(src, pdu, max_pdu, timeout);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACDL_BSC)
|
||||
case DATALINK_BSC:
|
||||
bytes = bsc_receive(src, pdu, max_pdu, timeout);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@@ -227,6 +251,11 @@ void datalink_cleanup(void)
|
||||
case DATALINK_MSTP:
|
||||
dlmstp_cleanup();
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACDL_BSC)
|
||||
case DATALINK_BSC:
|
||||
bsc_cleanup();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@@ -262,6 +291,11 @@ void datalink_get_broadcast_address(BACNET_ADDRESS *dest)
|
||||
case DATALINK_MSTP:
|
||||
dlmstp_get_broadcast_address(dest);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACDL_BSC)
|
||||
case DATALINK_BSC:
|
||||
bsc_get_broadcast_address(dest);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@@ -297,6 +331,11 @@ void datalink_get_my_address(BACNET_ADDRESS *my_address)
|
||||
case DATALINK_MSTP:
|
||||
dlmstp_get_my_address(my_address);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACDL_BSC)
|
||||
case DATALINK_BSC:
|
||||
bsc_get_my_address(my_address);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@@ -333,6 +372,11 @@ void datalink_set_interface(char *ifname)
|
||||
case DATALINK_MSTP:
|
||||
(void)ifname;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACDL_BSC)
|
||||
case DATALINK_BSC:
|
||||
(void)ifname;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@@ -365,6 +409,11 @@ void datalink_maintenance_timer(uint16_t seconds)
|
||||
#if defined(BACDL_MSTP)
|
||||
case DATALINK_MSTP:
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACDL_BSC)
|
||||
case DATALINK_BSC:
|
||||
bsc_maintenance_timer(seconds);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
#include "bacnet/basic/bbmd6/h_bbmd6.h"
|
||||
#endif
|
||||
|
||||
#if defined(BACDL_BSC)
|
||||
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||
#include "bacnet/datalink/bsc/bsc-datalink.h"
|
||||
#endif
|
||||
|
||||
#if defined(BACDL_ETHERNET) && !defined(BACDL_MULTIPLE)
|
||||
#define MAX_MPDU ETHERNET_MPDU_MAX
|
||||
|
||||
@@ -99,6 +104,17 @@ void routed_get_my_address(BACNET_ADDRESS *my_address);
|
||||
#define datalink_get_my_address bip6_get_my_address
|
||||
#define datalink_maintenance_timer(s) bvlc6_maintenance_timer(s)
|
||||
|
||||
#elif defined(BACDL_BSC) && !defined(BACDL_MULTIPLE)
|
||||
#define MAX_MPDU BVLC_SC_NPDU_SIZE_CONF
|
||||
|
||||
#define datalink_init bsc_init
|
||||
#define datalink_send_pdu bsc_send_pdu
|
||||
#define datalink_receive bsc_receive
|
||||
#define datalink_cleanup bsc_cleanup
|
||||
#define datalink_get_broadcast_address bsc_get_broadcast_address
|
||||
#define datalink_get_my_address bsc_get_my_address
|
||||
#define datalink_maintenance_timer(s) bsc_maintenance_timer(s)
|
||||
|
||||
#elif !defined(BACDL_TEST) /* Multiple, none or custom datalink */
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
|
||||
@@ -26,6 +26,14 @@
|
||||
#if (BACNET_PROTOCOL_REVISION >= 17)
|
||||
#include "bacnet/basic/object/netport.h"
|
||||
#endif
|
||||
#if defined(BACDL_BSC)
|
||||
#include "bacnet/basic/object/bacfile.h"
|
||||
#include "bacnet/basic/object/sc_netport.h"
|
||||
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||
#include "bacnet/datalink/bsc/bsc-util.h"
|
||||
#include "bacnet/datalink/bsc/bsc-datalink.h"
|
||||
#include "bacnet/datalink/bsc/bsc-event.h"
|
||||
#endif
|
||||
|
||||
/** @file dlenv.c Initialize the DataLink configuration. */
|
||||
/* timer used to renew Foreign Device Registration */
|
||||
@@ -459,6 +467,179 @@ void dlenv_network_port_init(void)
|
||||
since they are already set */
|
||||
Network_Port_Changes_Pending_Set(instance, false);
|
||||
}
|
||||
#elif defined(BACDL_BSC)
|
||||
/**
|
||||
* @brief Datalink network port object settings
|
||||
* @param primary_hub_uri
|
||||
* @param failover_hub_uri
|
||||
* @param filename_ca_1_cert
|
||||
* @param filename_ca_2_cert
|
||||
* @param filename_cert
|
||||
* @param filename_key
|
||||
* @param direct_connect_port
|
||||
* @param hub_function_port
|
||||
* @param direct_connect_initiate
|
||||
* @param direct_connect_accept_urls
|
||||
*/
|
||||
static void bacnet_secure_connect_network_port_init(
|
||||
char *primary_hub_uri,
|
||||
char *failover_hub_uri,
|
||||
char *filename_ca_1_cert,
|
||||
char *filename_ca_2_cert,
|
||||
char *filename_cert,
|
||||
char *filename_key,
|
||||
char *direct_binding,
|
||||
char *hub_binding,
|
||||
char *direct_connect_initiate,
|
||||
char *direct_connect_accept_urls)
|
||||
{
|
||||
const uint32_t instance = 1;
|
||||
BACNET_SC_UUID uuid = { 0 };
|
||||
BACNET_SC_VMAC_ADDRESS vmac = { 0 };
|
||||
long seed;
|
||||
char c;
|
||||
|
||||
seed = (long)&instance;
|
||||
srand((int)seed);
|
||||
Network_Port_Object_Instance_Number_Set(0, instance);
|
||||
Network_Port_Name_Set(instance, "BACnet/BSC Port");
|
||||
Network_Port_Type_Set(instance, PORT_TYPE_BSC);
|
||||
|
||||
bsc_generate_random_uuid(&uuid);
|
||||
Network_Port_SC_Local_UUID_Set(instance, (BACNET_UUID *)&uuid);
|
||||
bsc_generate_random_vmac(&vmac);
|
||||
Network_Port_MAC_Address_Set(instance, vmac.address, sizeof(vmac));
|
||||
|
||||
/* common NP data */
|
||||
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);
|
||||
|
||||
/* SC parameters */
|
||||
Network_Port_Max_BVLC_Length_Accepted_Set(instance, SC_NETPORT_BVLC_MAX);
|
||||
Network_Port_Max_NPDU_Length_Accepted_Set(instance, SC_NETPORT_NPDU_MAX);
|
||||
Network_Port_SC_Connect_Wait_Timeout_Set(
|
||||
instance, SC_NETPORT_CONNECT_TIMEOUT);
|
||||
Network_Port_SC_Heartbeat_Timeout_Set(
|
||||
instance, SC_NETPORT_HEARTBEAT_TIMEOUT);
|
||||
Network_Port_SC_Disconnect_Wait_Timeout_Set(
|
||||
instance, SC_NETPORT_DISCONNECT_TIMEOUT);
|
||||
Network_Port_SC_Maximum_Reconnect_Time_Set(
|
||||
instance, SC_NETPORT_RECONNECT_TIME);
|
||||
|
||||
if (filename_ca_1_cert == NULL) {
|
||||
fprintf(stderr, "BACNET_SC_ISSUER_1_CERTIFICATE_FILE must be set\n");
|
||||
return;
|
||||
}
|
||||
bacfile_create(BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE);
|
||||
bacfile_pathname_set(
|
||||
BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE, filename_ca_1_cert);
|
||||
Network_Port_Issuer_Certificate_File_Set(
|
||||
instance, 0, BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE);
|
||||
|
||||
if (filename_ca_2_cert) {
|
||||
bacfile_create(BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE);
|
||||
bacfile_pathname_set(
|
||||
BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE, filename_ca_2_cert);
|
||||
Network_Port_Issuer_Certificate_File_Set(
|
||||
instance, 1, BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE);
|
||||
}
|
||||
|
||||
if (filename_cert == NULL) {
|
||||
fprintf(stderr, "BACNET_SC_OPERATIONAL_CERTIFICATE_FILE must be set\n");
|
||||
return;
|
||||
}
|
||||
bacfile_create(BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE);
|
||||
bacfile_pathname_set(
|
||||
BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE, filename_cert);
|
||||
Network_Port_Operational_Certificate_File_Set(
|
||||
instance, BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE);
|
||||
|
||||
if (filename_key == NULL) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE must be set\n");
|
||||
return;
|
||||
}
|
||||
bacfile_create(BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE);
|
||||
bacfile_pathname_set(
|
||||
BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE, filename_key);
|
||||
Network_Port_Certificate_Key_File_Set(
|
||||
instance, BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE);
|
||||
|
||||
if ((primary_hub_uri == NULL) && (failover_hub_uri == NULL) &&
|
||||
(direct_binding == NULL) && (hub_binding == NULL)) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"At least must be set:\n"
|
||||
"BACNET_SC_HUB_FUNCTION_BINDING for HUB or\n"
|
||||
"BACNET_SC_PRIMARY_HUB_URI and BACNET_SC_FAILOVER_HUB_URI for node "
|
||||
"or\n"
|
||||
"BACNET_SC_DIRECT_CONNECT_BINDING for direct connect.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Network_Port_SC_Primary_Hub_URI_Set(instance, primary_hub_uri);
|
||||
Network_Port_SC_Failover_Hub_URI_Set(instance, failover_hub_uri);
|
||||
|
||||
Network_Port_SC_Direct_Connect_Binding_Set(instance, direct_binding);
|
||||
Network_Port_SC_Direct_Connect_Accept_Enable_Set(
|
||||
instance, direct_binding != NULL);
|
||||
|
||||
c = direct_connect_initiate ? direct_connect_initiate[0] : '0';
|
||||
if ((c != '0') && (c != 'n') && (c != 'N')) {
|
||||
Network_Port_SC_Direct_Connect_Initiate_Enable_Set(instance, true);
|
||||
} else {
|
||||
Network_Port_SC_Direct_Connect_Initiate_Enable_Set(instance, false);
|
||||
}
|
||||
|
||||
Network_Port_SC_Direct_Connect_Accept_URIs_Set(
|
||||
instance, direct_connect_accept_urls);
|
||||
|
||||
/* HUB parameters */
|
||||
Network_Port_SC_Hub_Function_Binding_Set(instance, hub_binding);
|
||||
Network_Port_SC_Hub_Function_Enable_Set(instance, hub_binding != NULL);
|
||||
|
||||
/* last thing - clear pending changes - we don't want to set these
|
||||
since they are already set */
|
||||
Network_Port_Changes_Pending_Set(instance, false);
|
||||
}
|
||||
|
||||
static bool dlenv_hub_connection_status_check(void)
|
||||
{
|
||||
uint32_t instance = Network_Port_Index_To_Instance(0);
|
||||
BACNET_SC_HUB_CONNECTION_STATUS *status;
|
||||
|
||||
status = Network_Port_SC_Primary_Hub_Connection_Status(instance);
|
||||
if (status && status->State == BACNET_SC_CONNECTION_STATE_CONNECTED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
status = Network_Port_SC_Failover_Hub_Connection_Status(instance);
|
||||
if (status && status->State == BACNET_SC_CONNECTION_STATE_CONNECTED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Datalink network port object settings for BACnet/SC
|
||||
*/
|
||||
void dlenv_network_port_init(void)
|
||||
{
|
||||
/* if a user has configured BACnet/SC port with primary hub URI, */
|
||||
/* wait for a establishin of a connection to BACnet/SC hub at first */
|
||||
/* to reduce possibility of packet losses. */
|
||||
if (Network_Port_SC_Primary_Hub_URI_char(1)) {
|
||||
while (!dlenv_hub_connection_status_check()) {
|
||||
bsc_wait(1);
|
||||
bsc_maintenance_timer(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* Datalink network port object settings
|
||||
@@ -548,6 +729,24 @@ void dlenv_maintenance_timer(uint16_t elapsed_seconds)
|
||||
* - BACNET_BIP6_PORT - UDP/IP port number (0..65534) used for BACnet/IPv6
|
||||
* communications. Default is 47808 (0xBAC0).
|
||||
* - BACNET_BIP6_BROADCAST - FF05::BAC0 or FF02::BAC0 or ...
|
||||
* - BACDL_BSC: (BACnet Secure Connect)
|
||||
* - BACNET_SC_PRIMARY_HUB_URI
|
||||
* - BACNET_SC_FAILOVER_HUB_URI
|
||||
* - BACNET_SC_ISSUER_1_CERTIFICATE_FILE
|
||||
* - BACNET_SC_ISSUER_2_CERTIFICATE_FILE
|
||||
* - BACNET_SC_OPERATIONAL_CERTIFICATE_FILE
|
||||
* - BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE
|
||||
* - BACNET_SC_DIRECT_CONNECT_BINDING - pair: interface name (optional) and
|
||||
* TCP/IP port number (0..65534), like "50000" (port only) or
|
||||
* "eth0:50000"(both)
|
||||
* - BACNET_SC_HUB_FUNCTION_BINDING - pair: interface name (optional) and
|
||||
* TCP/IP port number (0..65534), like "50000" (port only) or
|
||||
* "eth0:50000"(both)
|
||||
* - BACNET_SC_DIRECT_CONNECT_INITIATE - if true equal "1", "y", "Y",
|
||||
* otherwise false
|
||||
* - BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS - list of direct connect accept URLs
|
||||
* separated by a space character, e.g.
|
||||
* "wss://192.0.0.1:40000 wss://192.0.0.2:6666"
|
||||
*/
|
||||
void dlenv_init(void)
|
||||
{
|
||||
@@ -574,6 +773,8 @@ void dlenv_init(void)
|
||||
datalink_set("ethernet");
|
||||
#elif defined(BACDL_ARCNET)
|
||||
datalink_set("arcnet");
|
||||
#elif defined(BACDL_BSC)
|
||||
datalink_set("bsc");
|
||||
#else
|
||||
datalink_set("none");
|
||||
#endif
|
||||
@@ -665,6 +866,35 @@ void dlenv_init(void)
|
||||
} else {
|
||||
dlmstp_set_mac_address(127);
|
||||
}
|
||||
#elif defined(BACDL_BSC)
|
||||
char *primary_hub_uri;
|
||||
char *failover_hub_uri;
|
||||
char *filename_ca_1_cert;
|
||||
char *filename_ca_2_cert;
|
||||
char *filename_cert;
|
||||
char *filename_key;
|
||||
char *direct_binding;
|
||||
char *hub_binding;
|
||||
char *direct_connect_initiate;
|
||||
char *direct_connect_accept_urls;
|
||||
|
||||
primary_hub_uri = getenv("BACNET_SC_PRIMARY_HUB_URI");
|
||||
failover_hub_uri = getenv("BACNET_SC_FAILOVER_HUB_URI");
|
||||
filename_ca_1_cert = getenv("BACNET_SC_ISSUER_1_CERTIFICATE_FILE");
|
||||
filename_ca_2_cert = getenv("BACNET_SC_ISSUER_2_CERTIFICATE_FILE");
|
||||
filename_cert = getenv("BACNET_SC_OPERATIONAL_CERTIFICATE_FILE");
|
||||
filename_key = getenv("BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE");
|
||||
direct_binding = getenv("BACNET_SC_DIRECT_CONNECT_BINDING");
|
||||
hub_binding = getenv("BACNET_SC_HUB_FUNCTION_BINDING");
|
||||
direct_connect_initiate = getenv("BACNET_SC_DIRECT_CONNECT_INITIATE");
|
||||
direct_connect_accept_urls = getenv("BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS");
|
||||
bacnet_secure_connect_network_port_init(
|
||||
primary_hub_uri, failover_hub_uri, filename_ca_1_cert,
|
||||
filename_ca_2_cert, filename_cert, filename_key, direct_binding,
|
||||
hub_binding, direct_connect_initiate, direct_connect_accept_urls);
|
||||
if (!bsc_cert_files_check()) {
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
pEnv = getenv("BACNET_APDU_TIMEOUT");
|
||||
if (pEnv) {
|
||||
|
||||
@@ -391,6 +391,8 @@ static const int Properties_BACnetARRAY[] = {
|
||||
PROP_WEEKLY_SCHEDULE,
|
||||
PROP_EXCEPTION_SCHEDULE,
|
||||
PROP_TAGS,
|
||||
PROP_ISSUER_CERTIFICATE_FILES,
|
||||
PROP_SC_HUB_FUNCTION_ACCEPT_URIS,
|
||||
-1
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief BACnet Secure Connect encode and decode functions.
|
||||
* @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
|
||||
* @date 2023
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_SECURE_CONNECT_H
|
||||
#define BACNET_SECURE_CONNECT_H
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/datetime.h"
|
||||
#include "bacnet/datalink/bsc/bsc-conf.h"
|
||||
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Support
|
||||
- SC_Hub_Function_Connection_Status
|
||||
- SC_Direct_Connect_Connection_Status
|
||||
- SC_FailedConnectionRequests
|
||||
*/
|
||||
|
||||
typedef struct BACnetUUID_T_ {
|
||||
union {
|
||||
struct guid {
|
||||
uint32_t time_low;
|
||||
uint16_t time_mid;
|
||||
uint16_t time_hi_and_version;
|
||||
uint8_t clock_seq_and_node[8];
|
||||
} guid;
|
||||
uint8_t uuid128[16];
|
||||
} uuid;
|
||||
} BACNET_UUID;
|
||||
|
||||
#define BACNET_ERROR_STRING_LENGTH BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN
|
||||
#define BACNET_URI_LENGTH BSC_CONF_WSURL_MAX_LEN
|
||||
#define BACNET_PEER_VMAC_LENGTH BVLC_SC_VMAC_SIZE
|
||||
|
||||
enum BACNET_HOST_N_PORT_TYPE {
|
||||
BACNET_HOST_N_PORT_IP = 1,
|
||||
BACNET_HOST_N_PORT_HOST = 2
|
||||
};
|
||||
|
||||
typedef struct BACnetHostNPort_data_T {
|
||||
uint8_t type;
|
||||
char host[BACNET_URI_LENGTH];
|
||||
uint16_t port;
|
||||
} BACNET_HOST_N_PORT_DATA;
|
||||
|
||||
typedef struct BACnetSCHubConnectionStatus_T {
|
||||
BACNET_SC_CONNECTION_STATE State; /*index = 0 */
|
||||
BACNET_DATE_TIME Connect_Timestamp; /*index = 1 */
|
||||
BACNET_DATE_TIME Disconnect_Timestamp; /*index = 2 */
|
||||
/* optionals - use ERROR_CODE_DEFAULT for default value */
|
||||
BACNET_ERROR_CODE Error; /* index = 3 */
|
||||
char Error_Details[BACNET_ERROR_STRING_LENGTH]; /* index = 4 */
|
||||
} BACNET_SC_HUB_CONNECTION_STATUS;
|
||||
|
||||
typedef struct BACnetSCHubFunctionConnectionStatus_T {
|
||||
BACNET_SC_CONNECTION_STATE State;
|
||||
BACNET_DATE_TIME Connect_Timestamp;
|
||||
BACNET_DATE_TIME Disconnect_Timestamp;
|
||||
BACNET_HOST_N_PORT_DATA Peer_Address;
|
||||
uint8_t Peer_VMAC[BACNET_PEER_VMAC_LENGTH];
|
||||
BACNET_UUID Peer_UUID;
|
||||
BACNET_ERROR_CODE Error;
|
||||
char Error_Details[BACNET_ERROR_STRING_LENGTH];
|
||||
} BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS;
|
||||
|
||||
typedef struct BACnetSCFailedConnectionRequest_T {
|
||||
BACNET_DATE_TIME Timestamp;
|
||||
BACNET_HOST_N_PORT_DATA Peer_Address;
|
||||
uint8_t Peer_VMAC[BACNET_PEER_VMAC_LENGTH];
|
||||
BACNET_UUID Peer_UUID;
|
||||
BACNET_ERROR_CODE Error;
|
||||
char Error_Details[BACNET_ERROR_STRING_LENGTH];
|
||||
} BACNET_SC_FAILED_CONNECTION_REQUEST;
|
||||
|
||||
typedef enum BACnetRouterStatus {
|
||||
BACNET_ROUTER_STATUS_AVAILABLE = 0,
|
||||
BACNET_ROUTER_STATUS_BUSY = 1,
|
||||
BACNET_ROUTER_STATUS_DISCONNECTED = 2,
|
||||
BACNET_ROUTER_STATUS_MAX = 2
|
||||
} BACNET_ROUTER_STATUS;
|
||||
|
||||
typedef struct BACnetRouterEntry_T {
|
||||
uint16_t Network_Number;
|
||||
uint8_t Mac_Address[6];
|
||||
BACNET_ROUTER_STATUS Status;
|
||||
uint8_t Performance_Index;
|
||||
} BACNET_ROUTER_ENTRY;
|
||||
|
||||
typedef struct BACnetSCDirectConnectionStatus_T {
|
||||
char URI[BACNET_URI_LENGTH];
|
||||
BACNET_SC_CONNECTION_STATE State;
|
||||
BACNET_DATE_TIME Connect_Timestamp;
|
||||
BACNET_DATE_TIME Disconnect_Timestamp;
|
||||
BACNET_HOST_N_PORT_DATA Peer_Address;
|
||||
uint8_t Peer_VMAC[BACNET_PEER_VMAC_LENGTH];
|
||||
BACNET_UUID Peer_UUID;
|
||||
BACNET_ERROR_CODE Error;
|
||||
char Error_Details[BACNET_ERROR_STRING_LENGTH];
|
||||
} BACNET_SC_DIRECT_CONNECTION_STATUS;
|
||||
|
||||
/* */
|
||||
/* Encode / decode */
|
||||
/* */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_encode_SCHubConnection(
|
||||
uint8_t *apdu, const BACNET_SC_HUB_CONNECTION_STATUS *value);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_decode_SCHubConnection(
|
||||
const uint8_t *apdu,
|
||||
size_t apdu_size,
|
||||
BACNET_SC_HUB_CONNECTION_STATUS *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_encode_SCHubFunctionConnection(
|
||||
uint8_t *apdu, const BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *value);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_decode_SCHubFunctionConnection(
|
||||
const uint8_t *apdu,
|
||||
size_t apdu_size,
|
||||
BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_encode_SCFailedConnectionRequest(
|
||||
uint8_t *apdu, const BACNET_SC_FAILED_CONNECTION_REQUEST *value);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_decode_SCFailedConnectionRequest(
|
||||
const uint8_t *apdu,
|
||||
size_t apdu_size,
|
||||
BACNET_SC_FAILED_CONNECTION_REQUEST *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_encode_RouterEntry(uint8_t *apdu, const BACNET_ROUTER_ENTRY *value);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_decode_RouterEntry(
|
||||
const uint8_t *apdu, size_t apdu_size, BACNET_ROUTER_ENTRY *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_encode_SCDirectConnection(
|
||||
uint8_t *apdu, const BACNET_SC_DIRECT_CONNECTION_STATUS *value);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_decode_SCDirectConnection(
|
||||
const uint8_t *apdu,
|
||||
size_t apdu_size,
|
||||
BACNET_SC_DIRECT_CONNECTION_STATUS *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_snprintf_SCFailedConnectionRequest(
|
||||
char *str, size_t str_len, const BACNET_SC_FAILED_CONNECTION_REQUEST *req);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_snprintf_SCHubFunctionConnection(
|
||||
char *str,
|
||||
size_t str_len,
|
||||
const BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *st);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_snprintf_SCDirectConnection(
|
||||
char *str, size_t str_len, const BACNET_SC_DIRECT_CONNECTION_STATUS *st);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_snprintf_SCHubConnection(
|
||||
char *str, size_t str_len, const BACNET_SC_HUB_CONNECTION_STATUS *st);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
Reference in New Issue
Block a user