[package]
name = "sidm"
version = "0.3.0"
edition = "2024"
rust-version = "1.92"
license = "MIT OR Apache-2.0"
description = "PyDM-style EPICS display widgets and data engine, built on siplot"
repository = "https://github.com/physwkim/siplot"

# `sidm` ports PyDM's core engine + widgets (Python/Qt EPICS display manager)
# onto siplot's egui/wgpu plotting. egui/egui-wgpu are reached through siplot's
# re-export (`siplot::egui`) to avoid a duplicate egui/wgpu, exactly as the
# examples do. The EPICS backends (epics-ca-rs / epics-pva-rs) and the calc
# evaluator are feature-gated and wired in later waves so the headless engine
# core compiles with no network/runtime dependencies.

[features]
# `ca`/`pva` are on by default (Channel Access and pvAccess are the two EPICS
# transports, with in-process `CaServer`/`PvaServer` round-trip tests behind
# them); `calc` is also default-on (pure-Rust evalexpr, no network). `loc://`/
# `fake://` are always compiled, so `--no-default-features` gives the headless,
# dependency-light core.
default = ["ca", "pva", "calc"]
# Channel Access backend (`ca://`). Pulls epics-ca-rs (client + in-process
# CaServer for tests) and epics-base-rs (the `Snapshot`/`DbrClass`/`EpicsValue`
# types the client returns, which epics-ca-rs does not re-export).
ca = ["dep:epics-ca-rs", "dep:epics-base-rs"]
# pvAccess backend (`pva://`). Pulls epics-pva-rs (client + in-process PvaServer
# for tests); the NT value/structure types it returns live in epics-pva-rs
# itself, so no extra epics-base-rs surface is needed beyond what `ca` shares.
pva = ["dep:epics-pva-rs"]
# Derived-channel backend (`calc://`). Pulls evalexpr (a pure-Rust expression
# evaluator) to compute one channel from an expression over other channels'
# scalar values, porting PyDM's calc plugin. No network dependency.
calc = ["dep:evalexpr"]

[dependencies]
# `version` (alongside `path`) is required so `cargo publish` records a
# crates.io dependency on the published siplot; the path is used for local
# workspace builds. Keep this in lockstep with siplot's version.
siplot = { path = "..", version = "0.3.0" }
# Async runtime for the data engine. The connection tasks live on a tokio
# runtime the `Engine` owns; widgets read shared state on the GUI thread. `net`
# is enabled so `enable_all()` (and the EPICS backends, later) work.
tokio = { version = "1", features = [
    "rt-multi-thread",
    "sync",
    "time",
    "macros",
    "net",
] }
# CancellationToken — fired when the last Channel for a connection drops, to
# stop that connection's task deterministically.
tokio-util = "0.7"
# Static-image decoder for `SidmImage` (the MEDM `image` widget): decodes a
# GIF/PNG/TIFF file to RGBA for an egui texture. Already in the build graph (the
# vendored egui depends on it), so this adds no new crate to a full build; the
# format features are scoped to the three MEDM uses. Not behind a feature so the
# widget is always present (the `adl2sidm` converter emits it unconditionally).
image = { version = "0.25", default-features = false, features = [
    "gif",
    "png",
    "tiff",
] }

# EPICS Channel Access backend (feature `ca`). crates.io 0.18.x — an explicit
# deviation from siplot's no-new-dependency rule, scoped to this crate. The API
# is verified against the 0.18.6 source at ~/codes/epics-rs (same version).
epics-ca-rs = { version = "0.18", optional = true }
# `Snapshot`/`AlarmInfo`/`DisplayInfo`/`ControlInfo`/`EnumInfo`/`DbrClass`/
# `EpicsValue` returned by the CA client live here and are not re-exported by
# epics-ca-rs, so this is a direct dependency. Unified to the same 0.18.6 that
# epics-ca-rs pins, so there is one copy in the tree.
epics-base-rs = { version = "0.18", optional = true }
# pvAccess backend (feature `pva`). crates.io 0.18.x, same explicit deviation
# from siplot's no-new-dependency rule, scoped to this crate. The client/monitor
# API and the in-process `PvaServer` test harness are verified against the
# 0.18.6 source at ~/codes/epics-rs (same version). The NT value/structure types
# (`PvField`/`PvStructure`/`ScalarValue`/`NTScalar`) are re-exported by this
# crate, so no extra epics-base-rs surface is pulled in for `pva` alone.
epics-pva-rs = { version = "0.18", optional = true }
# Derived-channel expression evaluator (feature `calc`). Pure Rust, no network;
# evaluates a PyDM-style `calc://` expression over child channels' scalar values.
evalexpr = { version = "13", optional = true }
# Failed/discarded channel writes are logged through the standard `log` facade
# (PyDM's put_value "Unable to put …" logging); egui/eframe already log through
# the same facade, so the host app's logger picks both up. Dependency-free.
log = "0.4"

# X11 PRIMARY selection for the middle-click PV copy: PyDM sets
# `QClipboard.Selection` alongside the clipboard on Linux
# (pydm/widgets/base.py `show_address_tooltip`), and MEDM's Btn2 delivers the
# copy through PRIMARY exclusively (actions.c `selectionConvertProc`) — that is
# how operators middle-paste PV names into terminals. egui's `copy_text`
# reaches only the CLIPBOARD selection, so the copy plugin sets PRIMARY
# directly. Text-only (no `image-data`); eframe already carries arboard via
# egui-winit, so a full build gains no new crate.
[target.'cfg(target_os = "linux")'.dependencies]
arboard = { version = "3.6", default-features = false }

[dev-dependencies]
# Headless egui renderer for the widget render tests (alarm-border readback),
# pinned to the same 0.34.3 the siplot crate's tests use so there is one egui in
# the tree. The `wgpu` feature provides the GPU readback (`Harness::render`); the
# tests read pixels back and assert colours, the same empirical-verification
# pattern as siplot's `tests/mask_pointer_offset.rs` (no golden images).
egui_kittest = { version = "0.34.3", features = ["wgpu"] }
# Windowing for the runnable example panels (a wgpu `RenderState` + an event
# loop), pinned to the same 0.34 the siplot examples use. The library never
# depends on eframe — only the examples need a window.
eframe = "0.34"

# A panel of PyDM-style widgets driven entirely by `loc://`/`fake://` channels,
# so it runs with no IOC.
[[example]]
name = "sidm_local_panel"
required-features = []

# The same panel against live `ca://` PVs supplied on the command line.
[[example]]
name = "sidm_ca_panel"
required-features = ["ca"]

# A full control panel for the epics-rs mini-beamline IOC over live `ca://` PVs.
[[example]]
name = "sidm_mini_beamline"
required-features = ["ca"]
