Skip to content

Reading Devices

One-shot reads are the most basic operation supported by all backends. PACSys tries to optimize latency of this use case where possible, but some setup/teardown is still needed for every call. Consider streaming if you expect to read same data multiple times.


Simple API

The module-level functions use a shared global backend (DPM/HTTP, lazily initialized).

Single Value

import pacsys

value = pacsys.read("M:OUTTMP")      # Returns float, str, bytes, np.ndarray, or dict
print(f"Temperature: {value}")

read() raises DeviceError on failure. Use get() if you want to inspect errors without exceptions.

With Metadata

reading = pacsys.get("M:OUTTMP")

print(f"Value: {reading.value} {reading.units}")
print(f"Timestamp: {reading.timestamp}")
print(f"Type: {reading.value_type}")

if reading.is_error:
    print(f"Error: [{reading.facility_code},{reading.error_code}] {reading.message}")

Using the Device API, you can get a full Reading for any property:

from pacsys import Device

dev = Device("M:OUTTMP")
reading = dev.get(prop="setting")
print(f"Setpoint: {reading.value} at {reading.timestamp}")

reading = dev.get(prop="status", field="on")
print(f"On: {reading.value} at {reading.timestamp}")

The Reading object fields:

Field Description
value Device value (type depends on property)
value_type ValueType enum (SCALAR, SCALAR_ARRAY, RAW, TEXT, etc.) or None for error readings
units Engineering units string, or None
timestamp datetime when reading was taken
cycle Machine cycle number
is_success error_code == 0
is_warning error_code > 0 (data may still be usable)
is_error error_code < 0
ok error_code >= 0 and value is not None
name Device name extracted from DRF or metadata

Batch Reads

Read multiple devices in fewer network round-trips:

readings = pacsys.get_many([
    "M:OUTTMP",
    "G:AMANDA",
    "B:IRMS06[0:10]",
])

for reading in readings:
    if reading.ok:
        print(f"{reading.name}: {reading.value}")
    else:
        print(f"{reading.name}: ERROR - {reading.message}")

Results are returned in the same order as the input list. Devices that time out get a Reading with is_error=True.


Reading by Property

The qualifier character in the device address (or an explicit .PROPERTY suffix) determines what you read.

Scalar Reading (default)

# These are equivalent
pacsys.read("M:OUTTMP")
pacsys.read("M:OUTTMP.READING")

Returns float. value_type = ValueType.SCALAR.

Array Reading

import numpy as np

# Array range [start:end]
arr = pacsys.read("B:IRMS06[0:10]")    # np.ndarray, 11 elements
element = pacsys.read("B:IRMS06[0]")   # float, single element

Array reads return np.ndarray. value_type = ValueType.SCALAR_ARRAY.

Setting (setpoint)

# Read the current setpoint, not the measured value
pacsys.read("Z_ACLTST")              # _ qualifier = SETTING
pacsys.read("Z:ACLTST.SETTING")      # explicit property

# Array setpoint
pacsys.read("Z_ACLTS1[0:10]")

Returns float or np.ndarray. Same value types as reading.

Raw Bytes

raw = pacsys.read("M:OUTTMP.RAW")    # bytes object
print(f"Raw: {raw.hex()}")
print(f"Length: {len(raw)} bytes")

Returns bytes. value_type = ValueType.RAW. Raw reads return the unscaled binary value before any database transformation.

Description (text)

desc = pacsys.read("M~OUTTMP")       # ~ qualifier = DESCRIPTION
# "OUTSIDE TEMPERATURE"

Returns str. value_type = ValueType.TEXT.

Basic Status

status = pacsys.read("N|LGXS")       # | qualifier = STATUS
# {"on": True, "ready": False, "remote": True, "positive": True, "ramp": False}

Returns dict with five boolean keys. value_type = ValueType.BASIC_STATUS.

For richer status information, see Device Status.

Analog Alarm

alarm = pacsys.read("N@H801")        # @ qualifier = ANALOG alarm
# {"minimum": -10.0, "maximum": 10.0, "alarm_enable": True, ...}

Returns dict with alarm configuration. value_type = ValueType.ANALOG_ALARM.

See also Alarm Helpers for the AnalogAlarm class.

Digital Alarm

alarm = pacsys.read("N$H801")        # $ qualifier = DIGITAL alarm
# {"nominal": 7, "mask": 255, "alarm_enable": True, ...}

Returns dict. value_type = ValueType.DIGITAL_ALARM.

See also Alarm Helpers for the DigitalAlarm class.


Value Type Reference

DRF Qualifier Property Python Type ValueType
: or default READING float SCALAR
: with range READING np.ndarray SCALAR_ARRAY
_ SETTING float / np.ndarray SCALAR / SCALAR_ARRAY
.RAW RAW bytes RAW
~ DESCRIPTION str TEXT
| STATUS dict BASIC_STATUS
@ ANALOG alarm dict ANALOG_ALARM
$ DIGITAL alarm dict DIGITAL_ALARM

Explicit Backend

Instead of the global backend, create your own:

import pacsys

with pacsys.dpm() as backend:
    value = backend.read("M:OUTTMP")
    reading = backend.get("M:OUTTMP")
    readings = backend.get_many(["M:OUTTMP", "G:AMANDA"])

Any backend works the same way:

# ACL (read-only, no auth)
with pacsys.acl() as backend:
    value = backend.read("M:OUTTMP")

# DMQ (requires Kerberos for all operations)
from pacsys import KerberosAuth
with pacsys.dmq(auth=KerberosAuth()) as backend:
    value = backend.read("M:OUTTMP")

# gRPC
with pacsys.grpc() as backend:
    value = backend.read("M:OUTTMP")

See Backends for details on each backend.


Error Handling

from pacsys import DeviceError

# Option 1: exception-based
try:
    value = pacsys.read("Z:NOTFND")
except DeviceError as e:
    print(f"Facility: {e.facility_code}, Error: {e.error_code}")
    print(f"Message: {e.message}")

# Option 2: inspect Reading status
reading = pacsys.get("Z:NOTFND")
if reading.is_error:
    print(f"Error: {reading.message}")
elif reading.is_warning:
    print(f"Warning (data may be stale): {reading.value}")
else:
    print(f"Value: {reading.value}")

Common error codes:

Facility Error Meaning
17 (DPM) 1 DPM_PEND - device not found or no data
16 (DBM) -13 DBM_NOPROP - property doesn't exist for this device

See Also