Network Port objects
Answering ReinitializeDevice, which activates the changes clients write, is in ReinitializeDevice.
What the Network Port object models
Section titled “What the Network Port object models”A Network Port object describes one data-link port of the device: its network type (BACnet/IP, MS/TP,
BACnet/SC, a virtual network), its network number and, for each type, the port’s own settings such as the IP
address or the MS/TP Max_Master. A device with two data links has two Network Port objects. (CASBACnetStackDLL.h
calls Max_Master maxManager: BACnetPropertyIdentifier_maxManager, 64.)
The object needs the build option STACK_OPTION_OBJECT_TYPE_NETWORK_PORT. Handling ReinitializeDevice needs
STACK_OPTION_DM_RD_REINITIALIZE_DEVICE (or STACK_OPTION_DM_BR_BACKUP_AND_RESTORE, or
STACK_OPTION_NM_SCCM_A with BACnet/SC; any of them turns on STACK_OPTION_REINITIALIZE_DEVICE).
STACK_OPTION_TARGET_FULL turns all of these on.
Network types at a glance
Section titled “Network types at a glance”Each data link takes one Network Port object, with these values in BACnetStack_AddNetworkPortObject():
| Data link | networkType |
protocolLevel |
Connection string in the message callbacks | Chapter |
|---|---|---|---|---|
| BACnet/IPv4 | 5 (ipv4) | 2 (BACnet application) | 6 bytes: IPv4 address, then UDP port, big-endian | BACnet/IP |
| BACnet/IPv6 | 9 (ipv6) | 2 (BACnet application) | 18 bytes: IPv6 address, then UDP port, big-endian | Data links overview |
| MS/TP | 2 (mstp) | 2 only | 1 byte: the MS/TP MAC address | BACnet MS/TP |
| BACnet/SC | 11 (secureConnect) | 2 only | The WebSocket URI, at most 255 bytes | BACnet/SC |
Pass 4194303 as referencePort for each of them: they are the lowest protocol layer. You do not add the
Network Port objects of a virtual network (network type 7): BACnetStack_AddVirtualNetwork() and
BACnetStack_AddDeviceToVirtualNetwork() create them, as Virtual devices and gateways
describes. The
Data links overview covers the message callbacks these ports share.
Adding a Network Port object
Section titled “Adding a Network Port object”Add the device first, then one Network Port object per port:
DllExport bool BACnetStack_AddNetworkPortObject( const uint32_t deviceInstance, const uint32_t objectInstance, const uint8_t networkType, const uint8_t protocolLevel, const uint16_t networkNumber, const uint8_t networkNumberQuality, const uint32_t referencePort);| Parameter | Meaning |
|---|---|
deviceInstance |
The device that owns the port. |
objectInstance |
The Network Port object’s instance, 0 to 4194302. 4194303 is rejected. This is the value your send and receive callbacks use as networkPortInstance. |
networkType |
A BACnetNetworkType value: 0 = Ethernet, 2 = MS/TP, 5 = IPv4, 7 = virtual, 9 = IPv6, 11 = BACnet/SC. ARCNET, PTP, LonTalk, Zigbee and serial are not implemented and the call returns false. |
protocolLevel |
A BACnetProtocolLevel value: 0 = physical, 1 = protocol, 2 = BACnet application, 3 = non-BACnet application. A BACnet data link is 2. MS/TP and BACnet/SC accept only 2, Ethernet only 0. |
networkNumber |
The port’s network number. Used only at protocol level 2; at other levels it is accepted and ignored, and the property does not exist. |
networkNumberQuality |
0 = unknown, 1 = learned, 2 = learned and configured, 3 = configured. Larger values are rejected. Pass 0, 0 if you do not assign network numbers. |
referencePort |
The instance of the Network Port object this one sits on, or 4194303 if this is the lowest protocol layer. |
The call returns false if the device does not exist, an argument is out of range, or the combination of network type and protocol level is not supported.
Which properties exist depends on the network type and protocol level. The full table, with the callback that
serves each property, is in the doc comment of BACnetStack_AddNetworkPortObject() in
source/CASBACnetStackDLL.h. In short:
- The stack serves
Network_Type,Protocol_Level,Reference_Port,Network_Number,Network_Number_Quality,Changes_Pending,Command,Command_Validation_Result,APDU_Length,MAC_Addressand, on a BACnet/SC port, every SC property. ForMAC_Addressthe stack needs your help on two port types: on an IPv4 port it builds the six octets from yourIP_AddressandBACnet_IP_UDP_Port, and on an MS/TP port it asks your octet-string Get callback for the one octet. - Your callbacks serve the rest, for example
Object_Name,IP_Address,IP_Subnet_Mask,BACnet_IP_UDP_Port,FD_BBMD_AddressandFD_Subscription_Lifetimeon an IPv4 port, andMax_MasterandMax_Info_Frameson an MS/TP port. - For most properties the stack asks your Get callback first and uses its own value only if your callback
does not answer, so you can override a stack-held value. The exceptions are
MAC_Address,APDU_Length,Network_Number,Changes_Pendingand every BACnet/SC property: the stack always answers those itself, and a value your callback gives for them is not used.
For BACnet/SC ports, Certificates covers the pending changes and the
Network Port Command callback, and Operations and troubleshooting
lists the SC properties.
The networkPortInstance
Section titled “The networkPortInstance”The message callbacks name the port by its Network Port object instance:
DllExport void BACnetStack_RegisterCallbackSendMessageForPort(uint16_t(*FPCallbackSendMessageForPort)(const uint8_t* message, const uint16_t messageLength, const uint8_t* connectionString, const uint8_t connectionStringLength, const uint32_t networkPortInstance, bool broadcast));DllExport void BACnetStack_RegisterCallbackReceiveMessageForPort(uint16_t(*FPCallbackReceiveMessageForPort)(uint8_t* message, const uint16_t maxMessageLength, uint8_t* sourceConnectionString, uint8_t* sourceConnectionStringLength, uint8_t* destinationConnectionString, uint8_t* destinationConnectionStringLength, const uint8_t maxConnectionStringLength, uint32_t* networkPortInstance));- In the send callback, use
networkPortInstanceto pick the socket or serial port to transmit on. - In the receive callback, set
*networkPortInstanceto the instance of the port the frame arrived on. BACNET_NETWORK_PORT_DEFAULT(4194303) means “the default port for this transport” and is unambiguous only when the build contains exactly one data link. A build with more than one (anySTACK_OPTION_TARGET_FULLbuild) must report a real instance; the stack logs an error and drops a frame reported with the default.- The callbacks carry no network type:
networkPortInstanceidentifies the port. ThenetworkTypeyou pass toBACnetStack_AddNetworkPortObject()(aBACnetNetworkType, IPv4 = 5) belongs to the Network Port object.
How property values reach the stack
Section titled “How property values reach the stack”Network Port properties go through the same property callbacks as every other object. Check objectType == 56
(Network Port) and objectInstance to tell your ports apart. The callbacks an IPv4 or MS/TP port uses:
| Callback (registration function) | Network Port properties |
|---|---|
BACnetStack_RegisterCallbackGetPropertyBool |
Out_Of_Service |
BACnetStack_RegisterCallbackGetPropertyEnumerated |
Reliability, except that the stack reports RENEW_FD_REGISTRATION_FAILURE itself after a failed foreign device renewal, unless the port is Out_Of_Service, until a registration is accepted or the port stops being a foreign device; the host type of FD_BBMD_Address |
BACnetStack_RegisterCallbackGetPropertyOctetString |
IP_Address, IP_Subnet_Mask, IP_Default_Gateway, IP_DNS_Server elements, the IP host of FD_BBMD_Address, the MS/TP MAC_Address |
BACnetStack_RegisterCallbackGetPropertyCharacterString |
Object_Name; a name host of FD_BBMD_Address |
BACnetStack_RegisterCallbackGetPropertyUnsignedInteger |
BACnet_IP_UDP_Port, the port of FD_BBMD_Address, FD_Subscription_Lifetime, Max_Master, Max_Info_Frames, the IP_DNS_Server element count |
BACnetStack_RegisterCallbackSetPropertyUnsignedInteger |
Activation of FD_Subscription_Lifetime, Max_Master, Max_Info_Frames, Network_Number and the port of FD_BBMD_Address |
BACnetStack_RegisterCallbackSetPropertyOctetString |
Activation of an IP host of FD_BBMD_Address, and of an MS/TP MAC_Address assigned by a You-Are |
BACnetStack_RegisterCallbackSetPropertyCharacterString, BACnetStack_RegisterCallbackSetPropertyNull |
Activation of a name host, or no host, of FD_BBMD_Address |
FD_BBMD_Address is assembled from several calls, all with propertyIdentifier 418
(BACnetPropertyIdentifier_fdBbmdAddress). BACnet_IP_Global_Address, once you enable it with
BACnetStack_SetPropertyEnabled, is assembled from the same calls with propertyIdentifier 407
(BACnetPropertyIdentifier_bacnetIpGlobalAddress), and it reads as host none, port 0 while you serve
nothing. The stack does not work out a global address itself: if your application does, make the property
read-only with BACnetStack_SetPropertyWritable.
CallbackGetPropertyEnumeratedwithuseArrayIndexfalse returns the host type: 0 = none, 1 = IP address, 2 = name.CallbackGetPropertyUnsignedIntegerwith array index 2 returns the UDP port.- Array index 1 returns the host: 4 octets through
CallbackGetPropertyOctetStringfor an IP address, or a string throughCallbackGetPropertyCharacterStringfor a name.
The Set callbacks receive the same array indexes at activation. If your application does not answer the host
type query, the stack answers FD_BBMD_Address from its own foreign-device registration when it has one. If it
does not answer FD_Subscription_Lifetime, the stack answers with the time-to-live of its own registration, or 0.
The full signatures are in source/CASBACnetStackDLL.h; each Get callback ends with uint32_t* errorCode.
Return false without touching errorCode when a property is not yours to answer.
Writable properties and Changes_Pending
Section titled “Writable properties and Changes_Pending”These Network Port properties are writable, and a successful write holds the value pending instead of applying it:
| Property | Identifier | Port | Range checked by the stack |
|---|---|---|---|
FD_BBMD_Address |
418 | IPv4 | — |
BACnet_IP_Global_Address |
407 | IPv4, once you enable it | an IP host must be 4 octets |
FD_Subscription_Lifetime |
419 | IPv4 | 0 to 65535 |
Max_Master |
64 | MS/TP | 0 to 127 |
Max_Info_Frames |
63 | MS/TP | 1 to 255 |
Network_Number |
425 | any, at protocol level 2 | 0 to 65534; writable when the device is a router |
BBMD_Accept_FD_Registrations |
413 | IPv4, only while the port is a BBMD | — |
IP_Address, IP_Subnet_Mask, IP_Default_Gateway |
400, 411, 401 | IPv4, once you make it writable; read-only while IP_DHCP_Enable reads TRUE |
4 octets |
IP_DHCP_Enable |
402 | IPv4, once you make it writable | — |
BACnet_IP_UDP_Port |
412 | IPv4, once you make it writable | 0 to 65535 |
IP_DNS_Server |
406 | IPv4, once you make it writable; read-only while IP_DHCP_Enable reads TRUE |
4 octets an element; the whole array at its length, or one element by index 1 to its length; the length does not change |
Link_Speed |
420 | any port that has it, once you make it writable; read-only while Link_Speed_Autonegotiate reads TRUE |
a finite rate of 0 or more bits per second; when Link_Speeds (421) is enabled, one of the rates it serves |
Link_Speed_Autonegotiate |
422 | any port that has it, once you make it writable | — |
While one of these is pending, a client’s read returns the written value, but the stack’s own uses of the port’s address keep the address in use. MAC_Address follows the new address once the change is activated. The BACnet/IP data link’s own address and a BBMD’s own BDT entry are read again when you add the port again or register the Get callbacks again; see When the stack reads the port’s own address.
The BACnet/SC properties have their own pending set; see the BACnet/SC chapter.
What happens on a write:
- The stack checks the value’s type and range and refuses a bad value with an error. A refused write leaves
Changes_Pendingunchanged. - The stack checks that the value could ever be activated:
CallbackReinitializeDevicemust be registered, and so must the Set callback that will take the value (CallbackSetPropertyUnsignedInteger,CallbackSetPropertyBoolforIP_DHCP_EnableandLink_Speed_Autonegotiate,CallbackSetPropertyRealforLink_Speed,CallbackSetPropertyOctetStringfor the addresses (called once per writtenIP_DNS_Serverelement, with its array index), or theFD_BBMD_Addresscallbacks for its host type). If one is missing the write is answered with Abort(other). - The stack stores the value as pending. Your Set callback is not called.
- From now on a read of that property returns the pending value without calling your Get callback, and
Changes_Pendingreads TRUE.
Your Get callbacks keep serving the value in use. Your application needs no pending storage and no
Changes_Pending flag of its own.
A client can drop the pending values of one port by writing DISCARD_CHANGES to its Command property, or ask
for a check without activating by writing VALIDATE_CHANGES. To take part in those, register
BACnetStack_RegisterCallbackNetworkPortCommand() and BACnetStack_RegisterCallbackValidateNetworkPortChanges();
both are optional for IPv4 and MS/TP ports.
Activating pending changes
Section titled “Activating pending changes”Pending values become active in one of two ways:
- a ReinitializeDevice request with the state
ACTIVATE_CHANGES(7) orWARMSTART(1), which your application answers as ReinitializeDevice describes, or - a call to
BACnetStack_ActivateNetworkPortPendingChanges()from your own code.
Any other state, COLDSTART included, leaves the values pending.
Activation is all or nothing. The stack calls your Set callback once for each pending value. Treat that call
as “this value is now in use”: apply it to the port and store it in non-volatile memory. If your Set callback
returns false for one value, the stack hands back every value you accepted in the same pass by calling the Set
callback again with the value that was in use before (for the host-served properties, the value your Get
callback reported just before the pass), nothing is activated, and Changes_Pending stays TRUE.
BACnetStack_ActivateNetworkPortPendingChanges() runs the same checks and the same activation without calling
CallbackReinitializeDevice, so no password is checked:
DllExport bool BACnetStack_ActivateNetworkPortPendingChanges(const uint32_t deviceInstance, const uint32_t networkPortInstance);Pass a Network Port instance to activate that port only, or 4194303 to activate every port of the device.
Call it from the thread that calls BACnetStack_Tick(), never from inside a stack callback. Call it once your
start-up configuration is done, so the device does not boot reporting Changes_Pending for configuration
nobody wrote over the network.
The stack does not re-register as a foreign device by itself. When an activated FD_BBMD_Address or
FD_Subscription_Lifetime changes the registration, call BACnetStack_SendRegisterForeignDevice() with the
new values after BACnetStack_Tick() returns:
DllExport bool BACnetStack_SendRegisterForeignDevice(const uint32_t networkPortInstance, const uint16_t timeToLive, const uint8_t* connectionString, const uint8_t connectionStringLength);The connection string is the BBMD’s 4-octet IP address followed by its UDP port, big-endian. The call needs
BACnetStack_RegisterCallbackGetSystemTime() registered. The BBMD and foreign devices chapter
covers registration in detail.
Example
Section titled “Example”The ReinitializeDevice chapter links a complete example on GitHub: a device whose pending Network Port changes are activated by its ReinitializeDevice handler.
Practical notes
Section titled “Practical notes”- Register the Set callbacks before clients can write. Without
CallbackSetPropertyUnsignedInteger, or withoutCallbackReinitializeDevice, writes to the pending properties are answered with Abort(other), because the value could never be activated. Changes_PendingandNetwork_Numberare not asked of your Get callbacks. The stack serves both, so there is nothing to answer for them.- Expect a Set call with the old value. When activation is rolled back, the stack calls your Set callback again with the value that was in use before the pass.
- On an MS/TP port in stack-owned byte mode, the stack adds
Subordinate_Proxy_Enable,Manual_Subordinate_Address_Binding,Auto_Subordinate_DiscoveryandSubordinate_Address_Bindingat the firstBACnetStack_Tick()in that mode, and serves them itself. A packet-mode MS/TP port has none of them. See BACnet MS/TP.
