The Analog Output object is used to command analog actuators (valve or
damper positioners, variable-speed drives, heating elements, etc. -- which may be
hardware output signals or anything similar) or to publish
analog setpoints and calculated control signals. The present_value is fully
commandable: writes go through a 16-level priority_array with a relinquish_default
fallback; the effective value is the highest-priority non-null entry (or the default).
In addition to commandability, the object supports COV reporting (tunable via
cov_increment) and can be placed out of service. When intrinsic_reporting: true
is supplied to create/4, OUT_OF_RANGE intrinsic event generation is enabled using
high_limit, low_limit and deadband. Typical companion properties are units,
min_present_value, max_present_value, resolution, and device_type.
Object Description (ASHRAE 135)
The Analog Output object type defines a standardized object whose properties represent the externally visible characteristics of an analog output.
Analog Output objects that support intrinsic reporting shall apply the OUT_OF_RANGE event algorithm.
Behaviour and Operation
Analog Output objects are commandable output objects. The effective present_value
(the value that the local application should drive to the actuator) is derived from
the priority_array: the highest-priority (lowest numeric slot) non-nil entry wins;
if none is set, relinquish_default is used. The library automatically keeps
present_value in sync whenever the priority array or relinquish default changes
(except while out_of_service is true).
Application code (the device server or control logic) must observe changes to
present_value (via polling, subscriptions, or by hooking object updates) and apply
the value to the target (hardware or anything similar). It must not write directly to
present_value through update_property/3 while out_of_service is false; use
set_priority/3 (or update the priority_array / relinquish_default properties)
instead. When out_of_service is true, the output is disconnected and a
test value may be forced directly into present_value.
reliability and status_flags reflect problems detected by the output channel or
feedback (if any). COV reporting on the present value is available and
rate-limited by cov_increment.
When created with intrinsic_reporting: true the OUT_OF_RANGE intrinsic alarming
properties (high_limit, low_limit, deadband, ...) are present and the object
will manage event state transitions accordingly.
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: Effective commanded analog value. Dev must: Never write directly (library rejects unlessout_of_service); useset_priority/3or update therelinquish_default. Your actuator driver reads the effectivepresent_value(usingget_output/2) and drives hardware.priority_array: The command sources.status_flags,out_of_service,reliability(with inhibit): Dev must:out_of_servicelifts PV protection for testing. Reliability from actuator (:no_output, etc). Thein_alarm,faultandout_of_servicebits ofstatus_flagsare automatically maintained by the object. Theoverriddenbit is a local matter (your feedback logic can set it, if appropriate).min_present_value,max_present_value,resolution,units,cov_increment: Dev must: Enforce in your hardware driver (limit outputs further to hardware limit)Intrinsic (high/low/deadband + event, and feedback_value for command_failure): Dev must: After PV changes or feedback, re-eval OUT_OF_RANGE or COMMAND_FAILURE; drive the event machine and notifications.
See the detailed commandability model notes below, plus runtime, protection, feedback, reliability, COV, intrinsic.
Commandability model (the core contract): Because the object always declares
both priority_array and relinquish_default, the generated code
treats :present_value as a derived property. The only supported ways to
change the commanded value are:
set_priority(obj, 1..16, value_or_nil)- preferred high-level APIupdate_property(obj, :priority_array, new_pa)orupdate_property(obj, :relinquish_default, val)Any attempt to callupdate_property(obj, :present_value, ...)whileout_of_service == falseis rejected by the library with a clear error. This is the opposite of pure input objects, where the server must supply the protection.
How the library keeps present_value in sync:
- on
create(if a PA is supplied) - on
add_propertywhen a PA/relinquish is added - on
update_propertywhen PA or relinquish changes (unlessout_of_service) - on
set_priorityThe recalc is simply: highest non-nil priority slot, elserelinquish_default. Whenout_of_serviceistrue, this auto-sync is suppressed, so you can force a synthetic command for testing while the real actuator is disconnected.
Your actuator driver responsibility: After any successful update that changes
the returned object's present_value, your driver (or a change subscriber /
GenServer) must read the new value and drive the hardware (DAC, PWM, 4-20 mA
loop, valve positioner …). You can poll the object, or better, have the object
store call a callback / publish on a PubSub topic when a commandable object
changes. The device_type field is a good place to store a human string that
your HMI can show ("0-10V damper actuator on AO-03").
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.
Intrinsic alarming (OUT_OF_RANGE on a commanded value): When enabled the
object carries high/low/deadband etc. After you (or a set_priority/3) change
the effective PV, run the OUT_OF_RANGE evaluation exactly as for an Analog Input.
This is useful for "commanded value is outside the safe range the actuator
should ever see".
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 analog 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.
See the generated moduledoc tables for which fields are readonly, required, have init_funs, validators, or implicit_relationships.
Intrinsic Reporting
When intrinsic_reporting: true is passed to create/4, the deadband,
high_limit and low_limit properties become active and the object
participates in intrinsic event reporting using the OUT_OF_RANGE algorithm.
COV Reporting
Change-of-value reporting is supported via the cov_increment property.
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 (writing prioritized to present_value).
Examples
Creating a simple Analog Output (commandable object):
iex> {:ok, ao} = BACnet.Protocol.ObjectTypes.AnalogOutput.create(1, "Valve Cmd", %{units: :percent}); ao.units
:percentEnabling intrinsic reporting (additional properties become available):
iex> {:ok, ao} = BACnet.Protocol.ObjectTypes.AnalogOutput.create(2, "Setpoint", %{
...> units: :degrees_celsius
...> }, intrinsic_reporting: true); is_number(ao.high_limit)
trueSee Also
The following part has been automatically generated.
Click to expand
This module defines a BACnet object of the type `analog_output`. The following properties are defined: | Property | Revision | Required | Readonly | Protected | Intrinsic | |----------|----------|----------|----------|-----------|-----------| | acked_transitions | | | X | | X | | cov_increment | | | | | | | deadband | | | | | X | | description | | | | | | | device_type | | | | | | | 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 | | high_limit | | | | | X | | limit_enable | | | | | X | | low_limit | | | | | X | | max_present_value | | | | | | | min_present_value | | | | | | | notification_class | | | | | X | | notify_type | | | | | X | | object_instance | | X | X | | | | object_name | | X | X | | | | out_of_service | | X | | | | | present_value | | X | | | | | priority_array | | X | X | | | | profile_location | 19 | | | | | | profile_name | | | | | | | reliability | | | | | | | reliability_evaluation_inhibit | | | | | | | relinquish_default | | X | | | | | resolution | | | X | | | | status_flags | | X | X | | | | tags | 19 | | | | | | time_delay | | | | | X | | time_delay_normal | | | | | X | | units | | X | | | | The following properties have additional semantics: | Property | Has Default | Has Init | Implicit Relationships | Validators | Annotations | |----------|-------------|----------|------------------------|------------|-------------| | cov_increment | X | | | | | | deadband | X | | | | | | event_algorithm_inhibit_ref | | | event_algorithm_inhibit | | | | high_limit | X | | | | | | low_limit | X | | | | | | present_value | X | | | Fun | | | profile_location | | | | Fun | `revision: 19` | | reliability | | | reliability_evaluation_inhibit | | | | relinquish_default | X | | | Fun | | | tags | | | | | `revision: 19` | | units | X | | | | | The following table shows the default values and/or init functions: | Property | Default Value | Init Function | |----------|---------------|---------------| | cov_increment | `0.1` | | | deadband | `0.0` | | | high_limit | `0.0` | | | low_limit | `0.0` | | | present_value | `0.0` | | | relinquish_default | `0.0` | | | units | `:no_units` | |Summary
Types
Common object options for creation - all are optional.
Options accepted when creating or configuring an Analog Output object.
Available property names for this object.
The structure for property errors.
Represents an Analog 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.
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
@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 aConstants.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 typeatomare 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" (usuallyEncodingstructs).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. SeeBACnet.Protocol.Constants.protocol_revision/0for 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. Valuetrueneither type nor value are validated. Value:valuemeans the type is still validated and only the value validator is not run (if present). The property'svalidator_funwill also be skipped.
@type object_opts() :: {:intrinsic_reporting, boolean()} | common_object_opts()
Options accepted when creating or configuring an Analog Output object.
In addition to the common options, Analog Output supports:
intrinsic_reporting- Enables the OUT_OF_RANGE intrinsic reporting properties (deadband, high_limit, low_limit).
@type property_name() ::
:acked_transitions
| :cov_increment
| :deadband
| :description
| :device_type
| :event_algorithm_inhibit
| :event_algorithm_inhibit_ref
| :event_detection_enable
| :event_enable
| :event_message_texts
| :event_message_texts_config
| :event_state
| :event_timestamps
| :high_limit
| :limit_enable
| :low_limit
| :max_present_value
| :min_present_value
| :notification_class
| :notify_type
| :object_instance
| :object_name
| :out_of_service
| :present_value
| :priority_array
| :profile_location
| :profile_name
| :reliability
| :reliability_evaluation_inhibit
| :relinquish_default
| :resolution
| :status_flags
| :tags
| :time_delay
| :time_delay_normal
| :units
Available property names for this object.
@type property_update_error() :: {:error, {error :: atom(), property :: BACnet.Protocol.Constants.property_identifier()}}
The structure for property errors.
@type t() :: %BACnet.Protocol.ObjectTypes.AnalogOutput{ _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, cov_increment: float() | nil, deadband: float() | nil, description: String.t() | nil, device_type: String.t() | 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, high_limit: float() | nil, limit_enable: BACnet.Protocol.LimitEnable.t() | nil, low_limit: float() | nil, max_present_value: float() | nil, min_present_value: float() | 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(), present_value: float(), priority_array: BACnet.Protocol.PriorityArray.t(float()), 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: float(), resolution: float() | nil, 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, units: BACnet.Protocol.Constants.engineering_unit() | (reserved_or_vendor_extension :: non_neg_integer()) }
Represents an Analog 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.
For commandable objects (objects with a priority array), the present value property is protected.
To get the physical state, call get_output/2 and the function gets the present value in respect
to the out of service state.
Functions
@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.
@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.
By default, a default cov_increment of 0.1 is used.
It is strongly advised to change this to something reasonable for the application.
@spec get_all_properties() :: [BACnet.Protocol.Constants.property_identifier()]
Auto generated function to get the names of all properties this object supports.
@spec get_annotation(property_name()) :: [term()]
Auto generated function to get the annotations for the given property name.
@spec get_annotations() :: [{name :: property_name(), values :: [term()]}]
Auto generated function to get the list of annotations for each property.
@spec get_cov_properties() :: [BACnet.Protocol.Constants.property_identifier()]
Auto generated function to get the names of properties used for COV reporting.
@spec get_intrinsic_properties() :: [BACnet.Protocol.Constants.property_identifier()]
Auto generated function to get the names of intrinsic properties.
@spec get_object_identifier(t()) :: BACnet.Protocol.ObjectIdentifier.t()
Get the BACnet object identifier.
@spec get_optional_properties() :: [BACnet.Protocol.Constants.property_identifier()]
Auto generated function to get the names of optional properties.
Get the logical state of the object from the present value property.
If the object is out of service, then the decoupled_output_state is returned.
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 0.0 - the safe state of physical outputs in typical environments.
Get the active priority value from the priority array, or nil.
@spec get_properties(t()) :: [BACnet.Protocol.Constants.property_identifier()]
Get the list of properties the object has.
@spec get_properties_type_map() :: map()
Auto generated function to get a map of property name to type.
@spec get_property( t(), BACnet.Protocol.Constants.property_identifier() | non_neg_integer() ) :: {:ok, term()} | property_update_error()
Get a property's value from an object.
@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.
@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.
@spec get_required_properties() :: [BACnet.Protocol.Constants.property_identifier()]
Auto generated function to get the names of required properties.
@spec has_property?(t(), BACnet.Protocol.Constants.property_identifier()) :: boolean()
Checks if the given object has the given property.
See BACnet.Protocol.ObjectsUtility.has_property?/2 for implementation details.
Checks if the given object has Intrinsic Reporting enabled.
@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.
@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.
@spec set_priority(t(), 1..16, float() | nil) :: {:ok, t()} | property_update_error()
Sets the given priority in the priority array of an object. This function also updates the present value.
@spec supports_intrinsic() :: boolean()
Auto generated function to check whether the object type supports intrinsic reporting.
@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.