BACnet.Protocol.ObjectTypes.BinaryOutput (bacstack v0.1.0-dev.1)

Copy Markdown View Source

The Binary Output object is used to command two-state actuators (relays, contactors, motor starters, solenoid valves, fans, pumps, etc. -- which may be hardware output signals or anything similar). Its present_value (ACTIVE/INACTIVE) is fully commandable through a priority array; the resulting command is translated to the output according to the polarity property (normal or reverse).

In addition to priority-based commandability and relinquish_default, the object provides a minimum_off_time / minimum_on_time interlock mechanism and can optionally perform an automatic feedback write to the feedback_value property. When intrinsic_reporting: true is used at creation the COMMAND_FAILURE algorithm plus associated event properties become active.

Object Description (ASHRAE 135)

The Binary Output object type defines a standardized object whose properties represent the externally visible characteristics of a binary output. A "binary output" is a physical device or hardware output that can be in only one of two distinct states.

Binary Output objects that support intrinsic reporting shall apply the COMMAND_FAILURE event algorithm.

Behaviour and Operation

Binary Output objects are commandable two-state output objects (relays, motor starters, etc.). The effective present_value (the logical command the local logic should act upon) is always derived from the priority_array + relinquish_default. The library keeps present_value synchronised automatically. Application code must react to changes in present_value (and polarity) and apply it to the target (hardware or anything similar) accordingly. It must use set_priority/3 (or write the priority array / relinquish default) rather than writing present_value directly.

While out_of_service is true, the output is disconnected from the object and a test command may be forced into present_value. Many binary outputs also support an optional feedback_value (useful for verifying that the commanded state was actually achieved).

Minimum on/off time interlocks (min_on_time, min_off_time) are maintained by the application layer. When intrinsic reporting is enabled the COMMAND_FAILURE algorithm can detect when the output fails to reach the commanded state (using feedback).

Developer Implementation Notes (geared to device server / application authors)

The generated code handles storage + basic mechanics (validation, implicit_relationships, readonly annotations as hints to your server, etc.). You must drive "special" live properties and side effects yourself, analogous to maintaining present_value on inputs via update_property/3 (never direct mutation). Read notes below + generated tables for details.

Special / live properties and expected developer behaviour

  • present_value: The effective commanded state (after priority arbitration or relinquish). Dev must: Use set_priority/3 or update relinquish. Your actuator driver typically calls the provided get_output/2 helper to read the effective value and drive hardware. On polarity change or relinquish, re-apply to hardware.

  • priority_array, relinquish_default: Command sources. Dev must: Write via the high level APIs; the macro syncs PV from highest priority or default.

  • status_flags: The in_alarm/fault/out_of_service bits of status_flags are auto-managed; overridden is local matter. Maintain feedback if used (and set overridden if appropriate).

  • change_of_state_count, change_of_state_time, elapsed_active_time, time_of_state_count_reset, time_of_active_time_reset: History counters for state transitions and active time. Dev must: On every actual logical state flip (after polarity), bump count, set time, accumulate elapsed if active. On reset writes to the reset times, snapshot current and clear counters. These are side-effect "live" history properties you maintain from state changes (analog to value_* in Accumulator).

  • active_text / inactive_text: Human strings. Dev must: Set at init; changes are just data.

  • feedback_value (if present/intrinsic): For command_failure alarming. Dev must: If supporting, your hardware feedback must update this.

  • Intrinsic event properties: For command failure or other. Dev must: Drive event evaluation after PV or feedback changes.

Commandability & protection: Same rules as any commandable: only set_priority/3 or writes to the PA/relinquish fields change the effective command. Direct PV writes are rejected by the library.

out_of_service for outputs: true means "the physical channel is isolated from this object; the value you see/write is for simulation only". Your driver must stop driving the real hardware (or put it in a safe state) while true. The present_value is still subject to command priorization. See also get_output/2.

Polarity and the physical world: The logical PV (what the priority array and relinquish_default produce) is translated by the polarity property before it becomes the "command the hardware should see". Your driver must apply the polarity when it drives the relay / contactor / etc. You can use get_output/2 to get the effective state your driver should apply.

Minimum on/off times: The min_on_time and min_off_time fields (in seconds) are stored on the object. It is your driver's job to remember when the output last changed state and to refuse (or delay) a command that would violate the interlock. The object uses a process-less architecture, so it can not enforce them. The driver should write to the priority 6 with the current command state and automatically lift priority 6 once time has passed.

Feedback and COMMAND_FAILURE: This is the classic "commanded vs. actual" pattern for binary outputs. If you set the auto_write_feedback opt flag, the library will copy the effective (logical) PV into feedback_value on every change. If the underlying hardware can report back the actual state, you should instead update the property yourself.

elapsed_active_time etc.: Same maintenance responsibility as on the corresponding Binary Input - every time the physical state changes you update the counters and timestamps on the output object (or the driver can maintain them and only write when they change).

Intrinsic on a binary output: COMMAND_FAILURE is the interesting one; it uses the feedback mechanism described above. OUT_OF_RANGE doesn't make much sense on a boolean; the enrollment would be on a different object that watches the consequence of this output.

Reliability for an output: You (the driver) set it based on actuator health, wiring, power, etc. A typical pattern is to have a background task that reads the real feedback (if any) and, if it doesn't match the commanded value for longer than a timeout, sets reliability to :process_error or similar (the fault bit in status_flags will be automatically updated by the object).

Remote objects: If _metadata.remote_object is set (populated by BACnet.Protocol.ObjectsUtility when reading from a remote device), all mutation operations (update_property, add_property, set_priority, etc.) will be rejected by the generated code. You can only read remote binary outputs.

Important invariant: after any call that returns {:ok, new_obj}, the new_obj.present_value is the value your hardware must be driving if NOT out_of_service (subject to polarity, scaling, min/max clamps you implement on top). If you ever see a mismatch between the object and reality for longer than your tolerance, raise the reliability / event.

The generated tables are the place to see which fields have implicit relationships and which annotations affect encoding.

Intrinsic Reporting

When intrinsic_reporting: true is passed to create/4, the object uses the COMMAND_FAILURE event algorithm and the related event reporting properties become active.

Commandability and Priority Arrays

As an output object this always has a priority_array together with relinquish_default. The present value is protected from direct modification and is normally only changed through the priority array.

Examples

Creating a Binary Output with descriptive state texts:

iex> {:ok, bo} = BACnet.Protocol.ObjectTypes.BinaryOutput.create(1, "Fan Cmd", %{active_text: "On", inactive_text: "Off"}); bo.active_text
"On"

Using the special options (auto feedback + intrinsic reporting):

iex> {:ok, bo} = BACnet.Protocol.ObjectTypes.BinaryOutput.create(2, "Pump", %{}, auto_write_feedback: true, intrinsic_reporting: true)
iex> {is_boolean(bo.feedback_value), bo.event_state}
{true, :normal}

See Also



The following part has been automatically generated.

Click to expand This module defines a BACnet object of the type `binary_output`. The following properties are defined: | Property | Revision | Required | Readonly | Protected | Intrinsic | |----------|----------|----------|----------|-----------|-----------| | acked_transitions | | | X | | X | | active_text | | | | | | | change_of_state_count | | | | | | | change_of_state_time | | | | | | | description | | | | | | | device_type | | | | | | | elapsed_active_time | | | | | | | event_algorithm_inhibit | | | | | X | | event_algorithm_inhibit_ref | | | | | X | | event_detection_enable | | | | | X | | event_enable | | | | | X | | event_message_texts | | | X | | X | | event_message_texts_config | | | | | X | | event_state | | X | | | | | event_timestamps | | | X | | X | | feedback_value | | | | | X | | inactive_text | | | | | | | limit_enable | | | | | X | | min_off_time | | | | | | | min_on_time | | | | | | | notification_class | | | | | X | | notify_type | | | | | X | | object_instance | | X | X | | | | object_name | | X | X | | | | out_of_service | | X | | | | | polarity | | X | | | | | present_value | | X | | | | | priority_array | | X | X | | | | profile_location | 19 | | | | | | profile_name | | | | | | | reliability | | | | | | | reliability_evaluation_inhibit | | | | | | | relinquish_default | | X | | | | | status_flags | | X | X | | | | tags | 19 | | | | | | time_delay | | | | | X | | time_delay_normal | | | | | X | | time_of_active_time_reset | | | | | | | time_of_state_count_reset | | | | | | The following properties have additional semantics: | Property | Has Default | Has Init | Implicit Relationships | Validators | Annotations | |----------|-------------|----------|------------------------|------------|-------------| | active_text | X | | inactive_text | | | | change_of_state_count | X | | | | | | change_of_state_time | X | | change_of_state_count | | | | elapsed_active_time | X | | time_of_active_time_reset | Type | | | event_algorithm_inhibit_ref | | | event_algorithm_inhibit | | | | feedback_value | X | | | | `encode_as: :enumerated` | | inactive_text | X | | | | | | min_off_time | | | | Type | | | min_on_time | | | | Type | | | polarity | X | | | | | | present_value | X | | | | `encode_as: :enumerated` | | profile_location | | | | Fun | `revision: 19` | | reliability | | | reliability_evaluation_inhibit | | | | relinquish_default | X | | | | `encode_as: :enumerated` | | tags | | | | | `revision: 19` | | time_of_active_time_reset | X | | | | | | time_of_state_count_reset | X | | | | | The following table shows the default values and/or init functions: | Property | Default Value | Init Function | |----------|---------------|---------------| | active_text | `"Active"` | | | change_of_state_count | `0` | | | change_of_state_time | `%BACnet.Protocol.BACnetDateTime{...}` | | | elapsed_active_time | `0` | | | inactive_text | `"Inactive"` | | | polarity | `:normal` | | | time_of_active_time_reset | `%BACnet.Protocol.BACnetDateTime{...}` | | | time_of_state_count_reset | `%BACnet.Protocol.BACnetDateTime{...}` | |

Summary

Types

Common object options for creation - all are optional.

Options accepted when creating or configuring a Binary Output object.

Available property names for this object.

The structure for property errors.

t()

Represents a Binary Output object. All keys should be treated as read-only, all updates should go only through update_property/3.

Functions

Adds an optional property to an object. Remote objects can not be mutated using this operation.

Creates a new object struct with the defined properties. Optional properties are not created when not given, only required, given and dependency properties are created. Properties with a value of nil are ignored.

Auto generated function to get the names of all properties this object supports.

Auto generated function to get the annotations for the given property name.

Auto generated function to get the list of annotations for each property.

Auto generated function to get the names of properties used for COV reporting.

Auto generated function to get the names of intrinsic properties.

Get the BACnet object identifier.

Auto generated function to get the names of optional properties.

Get the logical state of the object from the present value property in respect to the polarity.

Get the active priority value from the priority array, or nil.

Get the list of properties the object has.

Auto generated function to get a map of property name to type.

Get a property's value from an object.

Auto generated function to get the names of protected properties.

Auto generated function to get the names of readonly properties.

Auto generated function to get the names of required properties.

Checks if the given object has the given property.

Checks if the given object has Intrinsic Reporting enabled.

Checks if the given property is writable.

Removes an optional property from an object. This function is idempotent. Remote objects can not be mutated using this operation.

Sets the given priority in the priority array of an object. This function also updates the present value.

Auto generated function to check whether the object type supports intrinsic reporting.

Updates a property of an object. To update the priority array, use set_priority/3 instead.

Types

common_object_opts()

@type common_object_opts() ::
  {:allow_numeric_constants, boolean()}
  | {:allow_unknown_properties, boolean()}
  | {:ignore_unknown_properties, boolean()}
  | {:revision, BACnet.Protocol.Constants.protocol_revision()}
  | {:skip_property_validation_remote_object, boolean() | :value}

Common object options for creation - all are optional.

  • allow_numeric_constants - Constants are atoms and thus unknown constants or vendor extensions are integers and thus are rejected. Enabling this option will allow integers (non_neg_integer()) for properties with a Constants.type() spec.
  • allow_unknown_properties - Properties that are unknown to the object implementation are usually rejected. With this option, unknown properties (numeric identifiers usually means we dont know them) are accepted and put into a separate map. This does mean we can not validate or write them. Types of the values can be anything at this point. While you can read unknown properties with atom or integer as property identifier, you can only remove numeric unknown property identifiers from an object. Property identifiers of type atom are only accepted, if it is a remote object (object implementation is only enforced if it is a local object). Numeric property identifiers are accepted regardless of remote object or not. For remote objects, this means you have to write "raw values" (usually Encoding structs).
  • ignore_unknown_properties - Properties that are unknown to the object implementation are usually rejected. With this option, unknown properties get ignored, as if they were not specified.
  • revision - The BACnet protocol revision to check required properties against. Optional properties are regardless of revision available. See BACnet.Protocol.Constants.protocol_revision/0 for the available revisions.
  • skip_property_validation_remote_object - Skips property validation for remote objects. Sometimes it is possible that the value is invalid as per BACnet specification (i.e. value 0 for a multistate object), but you still want those to be represented. Value true neither type nor value are validated. Value :value means the type is still validated and only the value validator is not run (if present). The property's validator_fun will also be skipped.

object_opts()

@type object_opts() ::
  {:auto_write_feedback, boolean()}
  | {:intrinsic_reporting, boolean()}
  | common_object_opts()

Options accepted when creating or configuring a Binary Output object.

In addition to the common options, Binary Output supports:

  • auto_write_feedback - When enabled, the feedback_value is automatically kept in sync with present_value changes.
  • intrinsic_reporting - Enables COMMAND_FAILURE intrinsic reporting properties.

property_name()

@type property_name() ::
  :acked_transitions
  | :active_text
  | :change_of_state_count
  | :change_of_state_time
  | :description
  | :device_type
  | :elapsed_active_time
  | :event_algorithm_inhibit
  | :event_algorithm_inhibit_ref
  | :event_detection_enable
  | :event_enable
  | :event_message_texts
  | :event_message_texts_config
  | :event_state
  | :event_timestamps
  | :feedback_value
  | :inactive_text
  | :limit_enable
  | :min_off_time
  | :min_on_time
  | :notification_class
  | :notify_type
  | :object_instance
  | :object_name
  | :out_of_service
  | :polarity
  | :present_value
  | :priority_array
  | :profile_location
  | :profile_name
  | :reliability
  | :reliability_evaluation_inhibit
  | :relinquish_default
  | :status_flags
  | :tags
  | :time_delay
  | :time_delay_normal
  | :time_of_active_time_reset
  | :time_of_state_count_reset

Available property names for this object.

property_update_error()

@type property_update_error() ::
  {:error,
   {error :: atom(),
    property :: BACnet.Protocol.Constants.property_identifier()}}

The structure for property errors.

t()

@type t() :: %BACnet.Protocol.ObjectTypes.BinaryOutput{
  _metadata: internal_metadata(),
  _unknown_properties: %{
    optional(atom() | non_neg_integer()) =>
      term()
      | BACnet.Protocol.ApplicationTags.Encoding.t()
      | [BACnet.Protocol.ApplicationTags.Encoding.t()]
  },
  acked_transitions: BACnet.Protocol.EventTransitionBits.t() | nil,
  active_text: String.t() | nil,
  change_of_state_count: non_neg_integer() | nil,
  change_of_state_time: BACnet.Protocol.BACnetDateTime.t() | nil,
  description: String.t() | nil,
  device_type: String.t() | nil,
  elapsed_active_time: BACnet.Protocol.ApplicationTags.unsigned32() | nil,
  event_algorithm_inhibit: boolean() | nil,
  event_algorithm_inhibit_ref: BACnet.Protocol.ObjectPropertyRef.t() | nil,
  event_detection_enable: boolean() | nil,
  event_enable: BACnet.Protocol.EventTransitionBits.t() | nil,
  event_message_texts: BACnet.Protocol.EventMessageTexts.t() | nil,
  event_message_texts_config: BACnet.Protocol.EventMessageTexts.t() | nil,
  event_state:
    BACnet.Protocol.Constants.event_state()
    | (reserved_or_vendor_extension :: non_neg_integer()),
  event_timestamps: BACnet.Protocol.EventTimestamps.t() | nil,
  feedback_value: boolean() | nil,
  inactive_text: String.t() | nil,
  limit_enable: BACnet.Protocol.LimitEnable.t() | nil,
  min_off_time: BACnet.Protocol.ApplicationTags.unsigned32() | nil,
  min_on_time: BACnet.Protocol.ApplicationTags.unsigned32() | nil,
  notification_class: non_neg_integer() | nil,
  notify_type:
    BACnet.Protocol.Constants.notify_type()
    | (reserved_or_vendor_extension :: non_neg_integer())
    | nil,
  object_instance: non_neg_integer(),
  object_name: String.t(),
  out_of_service: boolean(),
  polarity:
    BACnet.Protocol.Constants.polarity()
    | (reserved_or_vendor_extension :: non_neg_integer()),
  present_value: boolean(),
  priority_array: BACnet.Protocol.PriorityArray.t(boolean()),
  profile_location: String.t() | nil,
  profile_name: String.t() | nil,
  reliability:
    BACnet.Protocol.Constants.reliability()
    | (reserved_or_vendor_extension :: non_neg_integer())
    | nil,
  reliability_evaluation_inhibit: boolean() | nil,
  relinquish_default: boolean(),
  status_flags: BACnet.Protocol.StatusFlags.t(),
  tags: BACnet.Protocol.BACnetArray.t(BACnet.Protocol.NameValue.t()) | nil,
  time_delay: non_neg_integer() | nil,
  time_delay_normal: non_neg_integer() | nil,
  time_of_active_time_reset: BACnet.Protocol.BACnetDateTime.t() | nil,
  time_of_state_count_reset: BACnet.Protocol.BACnetDateTime.t() | nil
}

Represents a Binary Output object. All keys should be treated as read-only, all updates should go only through update_property/3.

Properties which are for Intrinsic Reporting are nil, if disabled. If Intrinsic Reporting is enabled on the object, then the properties can not be nil.

The physical output decouples the present value and the polarity from the physical state. The present value reflects the logical state of the object. To get the physical state, call get_output/2 and the function gets the present value in respect to the polarity and respecting out of service state.

For commandable objects (objects with a priority array), the present value property is protected.

Functions

add_property(object, property, value)

@spec add_property(t(), BACnet.Protocol.Constants.property_identifier(), term()) ::
  {:ok, t()} | property_update_error()

Adds an optional property to an object. Remote objects can not be mutated using this operation.

Please note that properties of services can not be dynamically added and instead the object must be newly created using create/4.

create(instance_number, object_name, properties \\ %{}, opts \\ [])

@spec create(
  non_neg_integer(),
  String.t(),
  %{optional(property_name() | atom() | non_neg_integer()) => term()},
  [object_opts() | internal_metadata()]
) :: {:ok, t()} | property_update_error()

Creates a new object struct with the defined properties. Optional properties are not created when not given, only required, given and dependency properties are created. Properties with a value of nil are ignored.

Only properties that are required for specific services (i.e. Intrinsic Reporting) are automatically created.

get_all_properties()

@spec get_all_properties() :: [BACnet.Protocol.Constants.property_identifier()]

Auto generated function to get the names of all properties this object supports.

get_annotation(name)

@spec get_annotation(property_name()) :: [term()]

Auto generated function to get the annotations for the given property name.

get_annotations()

@spec get_annotations() :: [{name :: property_name(), values :: [term()]}]

Auto generated function to get the list of annotations for each property.

get_cov_properties()

@spec get_cov_properties() :: [BACnet.Protocol.Constants.property_identifier()]

Auto generated function to get the names of properties used for COV reporting.

get_intrinsic_properties()

@spec get_intrinsic_properties() :: [BACnet.Protocol.Constants.property_identifier()]

Auto generated function to get the names of intrinsic properties.

get_object_identifier(object)

@spec get_object_identifier(t()) :: BACnet.Protocol.ObjectIdentifier.t()

Get the BACnet object identifier.

get_optional_properties()

@spec get_optional_properties() :: [BACnet.Protocol.Constants.property_identifier()]

Auto generated function to get the names of optional properties.

get_output(object, decoupled_output_state \\ false)

@spec get_output(t(), boolean()) :: boolean()

Get the logical state of the object from the present value property in respect to the polarity.

If the object is out of service, then the decoupled_output_state is returned. The actual polarity is ignored for decoupled_output_state. The specification specifies that when the object is out of service, then the physical output is decoupled from the BACnet present value. The actual physical output state can then either hold its last value, go to a safe state or behaves according to local logic (-> defined as local matter). The default value is false - the safe state of physical outputs in typical environments.

get_priority_value(object)

@spec get_priority_value(t()) :: {priority :: 1..16, value :: boolean()} | nil

Get the active priority value from the priority array, or nil.

get_properties(object)

@spec get_properties(t()) :: [BACnet.Protocol.Constants.property_identifier()]

Get the list of properties the object has.

get_properties_type_map()

@spec get_properties_type_map() :: map()

Auto generated function to get a map of property name to type.

get_property(object, property)

Get a property's value from an object.

get_protected_properties()

@spec get_protected_properties() :: [BACnet.Protocol.Constants.property_identifier()]

Auto generated function to get the names of protected properties.

Protected is an annotation and the object modules prevent writing to this property directly in code. The protected properties are either written on creation or updated automatically depending on other properties being written to. Some properties are only written once at creation and never updated.

get_readonly_properties()

@spec get_readonly_properties() :: [BACnet.Protocol.Constants.property_identifier()]

Auto generated function to get the names of readonly properties.

Readonly is only an annotation that the property should be write protected on the BACnet side, there is no actual write protection in the object. This is a hint to the device server. If you need actual write protection, see protected.

get_required_properties()

@spec get_required_properties() :: [BACnet.Protocol.Constants.property_identifier()]

Auto generated function to get the names of required properties.

has_property?(object, property)

Checks if the given object has the given property.

See BACnet.Protocol.ObjectsUtility.has_property?/2 for implementation details.

intrinsic_reporting?(object)

@spec intrinsic_reporting?(t()) :: boolean()

Checks if the given object has Intrinsic Reporting enabled.

property_writable?(object, property)

@spec property_writable?(t(), BACnet.Protocol.Constants.property_identifier()) ::
  boolean()

Checks if the given property is writable.

Check BACnet.Protocol.ObjectsUtility.property_writable?/2 for a basic run-down.

remove_property(object, property)

@spec remove_property(
  t(),
  BACnet.Protocol.Constants.property_identifier() | non_neg_integer()
) ::
  {:ok, t()} | property_update_error()

Removes an optional property from an object. This function is idempotent. Remote objects can not be mutated using this operation.

Please note that properties of services can not be dynamically removed and instead the object must be newly created using create/4. Required properties can not be removed.

set_priority(object, priority, value)

@spec set_priority(t(), 1..16, boolean() | nil) ::
  {:ok, t()} | property_update_error()

Sets the given priority in the priority array of an object. This function also updates the present value.

supports_intrinsic()

@spec supports_intrinsic() :: boolean()

Auto generated function to check whether the object type supports intrinsic reporting.

update_property(object, property, value)

@spec update_property(t(), BACnet.Protocol.Constants.property_identifier(), term()) ::
  {:ok, t()} | property_update_error()

Updates a property of an object. To update the priority array, use set_priority/3 instead.