Skip to main content

ReinitializeHandler

Type Alias ReinitializeHandler 

Source
pub type ReinitializeHandler = Arc<dyn Fn(&ReinitializeContext, &mut ObjectDatabase) -> Result<(), Error> + Send + Sync>;
Expand description

Carries out a ReinitializeDevice request (Clause 16.4) on a full server or an endpoint session.

It is called only for a request that decoded, passed its password check and names a state the clause defines. Ok(()) sends the SimpleACK; an Err is sent in its place. The rules:

  • Reply first, restart later. The SimpleACK goes out only after the handler returns, so it must not restart the device inline. Accept or refuse, prepare, and register the restart (or the backup or restore step) with ReinitializeContext::after_reply: Clause 16.4.2 has the device acknowledge the request before it shuts down.
  • The after-reply hook. It runs once the SimpleACK has been handed to the transport (#1565). On MS/TP, handed off means queued: on the reply channel or the transmit queue the frame still waits for the token, so stopping the server or the port at once can drop the reply there. Wait a short while (a few token rotations) first. It never runs for a request answered with an Error: the handler refused it or panicked, or the Network Port activation below failed. Nor does it run when the reply could not be handed over; the requester retries then. BACnetServer::stop seals responses and aborts request tasks before joining them, so a restart path that stops the server starts from the hook, never from the handler. On a full server, accepted WARMSTART/COLDSTART ends DISABLE_INITIATION as the handler accepts (Clause 16.1.2), cancels its timer and resumes held COV, even if the reply later fails.
  • Be quick. It runs synchronously on a runtime worker with the object database write-locked, so every other request waits for it. Hand slow work, such as writing backup files, to a task. On an endpoint session a blocking handler stalls the whole session. Never wait on another task or thread from it (a block_on, a channel receive, a join): if that work needs the database, the server deadlocks.
  • Database edits are raw. On a full server, changes made through the &mut ObjectDatabase skip what BACnetServer::write_local adds around a write: the Object_Name uniqueness check, the COV fanout, the event pass, Schedule and Command follow-ups, and the Audit record. A change that needs them, as ACTIVATE_CHANGES work on Network Port or Device properties may, belongs in write_local, called after the reply from a task the handler schedules (for example by sending the change to the code that holds the server).
  • Network Port changes activate around it. For ACTIVATE_CHANGES or WARMSTART the server first checks that the values written to Network Port objects would still be taken (ObjectDatabase::check_network_port_changes), and refuses with PROPERTY / INVALID_CONFIGURATION_DATA (Clause 16.4.1.3.1) before the handler runs if not. Once the handler accepts, the server puts them into effect under the same lock and clears Changes_Pending (Clause 12.56.13), the transport’s BDT for one (ObjectDatabase::activate_network_port_changes): all or none per port, not across ports. A refusal then is answered the same way and leaves them pending. With no handler set, a registered port’s pending-activation rows refuse writes, since nothing could activate them.
  • Colour outputs on an in-process restart. A server start drives each Color and Color Temperature output to its default (#1526). An application that carries WARMSTART or COLDSTART out in process, without building a new server, should do the same: call start_output on each colour object, which is safe to call again (a zero default then keeps the current value). It is a raw database edit, so it sends no COV notification of its own.
  • Audited once accepted. With Audit Reporters configured, an accepted request (the handler accepted and any Network Port activation took) records DEVICE_RESET, DEVICE_BACKUP or DEVICE_RESTORE from the requester (Clause 19.6, Table 19-5, #1564); ACTIVATE_CHANGES, which the table doesn’t list, and every refusal record nothing. Report a reset made for the application’s own reasons with BACnetServer::report_device_reset_local.
  • Gate the requester. Without reinit_password any peer reaches the handler. Neither MutationPolicy nor the ServerPolicy covers ReinitializeDevice, so check the ReinitializeContext to restrict sources.
  • Refuse with Error::protocol. Its class and code go back in an Error PDU, for example DEVICE / CONFIGURATION_IN_PROGRESS (Clause 16.4.1.3.1). Once the handler has run, the request may no longer be rejected (Clause 20.1.8), so an Error::reject, or any error other than an Error PDU’s Remote or Structured, is sent as SERVICES / OTHER. A panic is caught (with unwind builds) and also answered SERVICES / OTHER, since the handler may have partly acted.

Aliased Type§

pub struct ReinitializeHandler { /* private fields */ }