opcda-bridge-gateway 0.5.8

OPC DA gateway (Windows COM server)
docs.rs failed to build opcda-bridge-gateway-0.5.8
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: opcda-bridge-gateway-0.3.2

opcda-bridge-gateway

Windows gateway that exposes native OPC DA (COM/DCOM) servers over the network to opcda-bridge-client and other gRPC clients.

The supported Windows gateway target is 32-bit x86 (i686-pc-windows-msvc), including on 64-bit Windows. This is deliberate: the gateway loads native OPC DA/COM components alongside the server, and legacy installations may expose those components only through the 32-bit COM and registry view. Windows runs this gateway through WOW64 on a 64-bit host. The x86 gateway is the supported Windows gateway; a host-default x64 build is not a substitute.

Install it with Cargo on a Windows host:

rustup target add i686-pc-windows-msvc
cargo install --locked --target i686-pc-windows-msvc opcda-bridge-gateway

The gateway is versioned independently from the client and protocol crates. Prebuilt archives use opcda-bridge-gateway-vX.Y.Z tags; client/gateway interoperability is determined by the wire protocol and advertised capabilities rather than matching package versions.

The gateway-wide GetGatewayInfo RPC advertises the protocol-feature ranges supported by the running binary without opening an OPC DA server. The client compatibility command uses this handshake for deployment checks; GetCapabilities remains the per-server operational capability endpoint and supports older clients.

The gateway must run on the Windows machine hosting the OPC DA server. See the repository README for service installation, configuration, and firewall setup.

Tag browsing uses native one-level OPC DA enumeration with bounded pages. The gateway owns opaque browse sessions and continuation tokens, preserves exact ItemIDs separately from display names, and reports whether a page is complete. Only selectable item and branch-and-item nodes expose ItemIDs; branch-only nodes retain their private navigation identifiers behind opaque node keys. Namespace search is a bounded progressive operation that can be cancelled by dropping the client stream. DA3 root ItemIDs and unused filters are sent as required non-null empty strings. A server that also supports DA2 falls back only when its first DA3 root browse returns RPC_X_NULL_REF_POINTER or E_NOTIMPL, and reports that compatibility decision explicitly.

The gateway also exposes persistent indexed-search status, refresh, pause, resume, cancel, delete, and query operations. A first manual refresh validates the exact ProgID against current server discovery, persists enrollment, and begins indexing immediately; an unknown ProgID is rejected without durable state. Capability responses advertise indexed-search support, its protocol version, the configured result limit, and the server's index state. Indexed results contain exact ItemIDs and breadcrumb labels, never browse-session node keys. Refreshes run asynchronously, and gateway shutdown cancels active indexing before the process exits. After an inventory reaches a terminal event, the gateway settles and drops the native inventory stream before it records the generation outcome or releases build ownership and the file lock. This keeps a subsequent build from starting while the previous native worker is still joining. Foreground operations are reference-counted per server; indexing stays paused while any foreground user is active and remains paused through the configured quiet period after the last foreground operation ends. An inventory can complete successfully with a non-fatal warning when the OPC server rejects specific namespace branches; the generation remains active and usable, and the status diagnostic is reported as a warning unless the index state is failed. Each build checks its maintenance-window, health, and adaptive recovery gates before requesting the next inventory event. Health readiness combines capability and latency checks with the optional sentinel tag; unhealthy targets pause inventory with bounded exponential backoff and healthy recovery resumes it with the configured pacing. Without a sentinel tag, the health status is reported as Unavailable while capability and latency checks can still permit the build. Cancellation, health failure, or a rejected pacing update terminates the build without replacing the last complete generation. Pending entries are flushed before terminal state is recorded, and successful completion with a non-fatal inventory warning remains searchable while the warning is exposed in status. Each terminal build emits one structured namespace index build telemetry log record. It includes the terminal outcome and timing, the last progress snapshot (last_progress_entries_seen and last_progress_unique_items), the authoritative persisted row count (persisted_items), active and paused time, inventory slice counts and elapsed-time totals, native operation totals, DA2/DA3 slice counts, entry-kind counts, SQLite commit counts and latency summaries, and any failure text. It also records drained_events, received_entry_events, pending_entries, and pending_unique_items. The first two count events consumed by the gateway and entry events received from the native stream; the latter two describe entries still buffered when terminal cleanup was reached. For an interrupted build, these fields make a difference between progress and persisted rows measurable rather than inferred from partial counts alone. The progress snapshot can differ from the persisted count because progress and terminal inventory events are asynchronous; use the promoted generation's persisted counts for acceptance and throughput calculations. Successful generation promotion emits a separate log record with its duration. These records are diagnostic only and do not change the index schema or runtime pacing. Completed active generations are durable across gateway restarts. Activation is an atomic metadata transition, and promotion status uses a read-only SQLite connection plus filesystem diagnostics, so status remains responsive even while the writer is in the promotion critical section. Only one build for a server can hold its gateway-wide file lock at a time; contention reports the owning process metadata. On Windows, that metadata is kept in an adjacent .build.owner sidecar because the locked file itself may be unreadable. The owner sidecar is removed on a clean lock release and can remain after forced termination until the next acquisition overwrites it; the operating-system advisory lock, not the sidecar's existence, determines whether a build is active. Superseded and abandoned data is reclaimed in bounded background batches through a separate SQLite WAL connection, coordinated by a database-wide writer gate shared with every build mutation for the same database file, including builds for other servers. Cleanup defers while any build is active, keeps its request registered while waiting, yields between batches so a build can make progress, and resumes pending requests after the last build finishes even when the request came from another manager instance sharing the database. Shutdown is also observed when it races with that deferred wait. An interrupted refresh is superseded when a complete active generation remains available, so status and search continue to use that snapshot while cleanup runs. An interrupted initial build remains failed and visible because no complete snapshot can replace it. If SQLite recovery quarantines an invalid index, the database and its -wal/-shm sidecars are kept together so committed data remains available for diagnosis. Status combines the persisted generation snapshot with runtime build, health, storage, foreground, and scheduler diagnostics. During promotion, persisted status is read through a read-only connection; a runtime error overrides the reported state only when no build is active. Database coordination and persistent build-lock paths use the canonical identity of the database file, so existing-file aliases such as relative paths and symlinks cannot bypass coordination. If the file and its parent cannot be canonicalized, the original path spelling is retained. Independent in-memory databases are isolated from the registry and do not create filesystem build-lock sidecars. Uncached indexed searches use a separate read-only SQLite connection and rank only bounded candidate sets in memory, so a broad query cannot hold the coordinator's foreground database mutex while it scans the FTS index. Exact searches use separate covering equality lookups on the normalized display-name and ItemID indexes, each bounded to limit + 1 rows, exclude lower-priority ItemID duplicates already found by the display-name probe, and then merge and deduplicate those candidates before ranking. Prefix searches use indexed lexicographic ranges rather than generation-wide LIKE scans; contains searches retain their FTS path. Status, discovery, reads, writes, and lazy browse therefore remain available while search work is in progress. Matching is case-insensitive with exact/prefix/contains ranking, and responses report when additional results exist beyond the requested limit. During promotion, searches use the active generation already returned by the promotion-safe status path instead of waiting for the writable database mutex. Refresh setup is staged before the asynchronous build task is launched. If startup, capability negotiation, generation creation, task launch, or shutdown fails at that boundary, the provisional generation is abandoned and its build reservation is released while the last complete active generation remains available. Cancellation requests received before inventory startup returns its control handle are retained and applied once the handle is available.

Read responses contain semantic values. For an OPC DA VT_BSTR, the gateway forwards the exact BSTR contents without adding display quote characters; quotes remain only when present in the server value.

Configure index-wide behavior in the gateway TOML file under [index]. SQLite owns enrolled servers and each server's auto-refresh setting; a fresh gateway has no enrolled servers and never starts an automatic first build. A successful manually enrolled index is refreshed weekly by default when its per-server auto-refresh setting and global index.enabled switch permit it. Disabling per-server auto-refresh preserves its searchable generation; deleting an index removes its enrollment, generations, entries, and retry state after coordinating any active build. Delete returns a temporary deleting status while cleanup runs, then reaches not-indexed. The gateway uses a service-writable SQLite database, conservative batch/rate/duty-cycle defaults, a two-second foreground quiet period, and one build at a time. Native inventory batches are bounded to 1,000 entries by the OPC DA client contract. Native inventory slicing and SQLite commit batching are independently bounded: the default SQLite commit batch is 1,024 entries, so a commit may span multiple native slices. Adaptive controller decisions update the native slice batch size and item-rate limiter; the native minimum operation interval remains a separate control and is zero for gateway-generated inventory pacing. The item-rate limiter is charged by the native operation's item cost and must not be converted into a batch-size-derived sleep. The commit interval provides a time limit for low-volume inventories, flushing pending entries even when the batch threshold has not been reached. index.worker_count controls independent namespace workers within one build (default 1, maximum 4). When it is greater than one, the gateway uses hierarchical root browsing to partition safe canonical branches across fresh root-scoped inventory streams. Root-level items are emitted directly and duplicate ItemIDs are suppressed. If the server cannot provide a complete, session-backed hierarchical root page with at least two expandable branches, the gateway falls back to one full-root inventory. Set index.inventory_root to an exact canonical ItemID to inventory only that subtree; this uses the client's root-scoped API directly and takes precedence over automatic root partitioning. Coordinated workers share pause, resume, pacing, and cancellation controls; all worker streams are shut down and joined before the build releases its ownership. Runtime status includes rolling foreground latency/error/quality metrics, host/storage availability, and persisted scheduler backoff diagnostics. If the native client rejects an initial or adaptive pacing update, the build fails visibly and the previous complete generation remains active; pacing errors are never logged and ignored. Completed enrolled generations are refreshed weekly by default. Maintenance windows, startup grace, and deterministic per-server schedule jitter prevent scheduled work from starting at unsuitable times or in lockstep across targets. The default database path is %PROGRAMDATA%\\opcda-bridge\\index.sqlite3 on Windows and $XDG_DATA_HOME/opcda-bridge/index.sqlite3 (falling back to $HOME/.local/share/opcda-bridge/index.sqlite3) on Linux/macOS. See the example file for all available settings, including maintenance windows, health thresholds, and adaptive AIMD rate/batch/duty-cycle floors and ceilings. Adaptive indexing starts at the canary profile and backs off or pauses when recent foreground OPC errors or bad-quality reads, or host/storage guardrails, deteriorate. Pre-build and health OPC operations are bounded by operation_timeout_seconds, so an unresponsive target cannot hold the scheduler indefinitely. An optional sentinel_tag is read during health probes; omitted or unavailable sentinel configuration is reported explicitly rather than treated as a healthy zero value. Status also distinguishes a configured sentinel from its probe result, so an unprobed sentinel is not reported as absent.

The gateway migrates the previous indexed-search database schema in place through the schema 2 -> 3 -> 4 chain. Existing generations, relational entries, and full-text search data are preserved; servers with a usable active generation are enrolled for automatic refresh, while failed-only histories remain visible and require a manual retry before scheduled refresh is enabled. Each migration step is transactional, so a failed upgrade is reported without leaving a partially upgraded index database.