pvxs-sys 0.1.1

Low-level FFI bindings for EPICS PVXS library
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
// wrapper.h - C++ wrapper layer to simplify PVXS for Rust FFI
// This layer handles the complex C++ patterns (callbacks, shared_ptr, etc.)

#pragma once

#include <memory>
#include <string>
#include <stdexcept>
#include <optional>
#include <chrono>
#include "rust/cxx.h" // For rust::String and rust::Str types

#include <pvxs/client.h>
#include <pvxs/server.h>
#include <pvxs/sharedpv.h>
#include <pvxs/nt.h>
#include <pvxs/log.h>

namespace pvxs_wrapper
{

    // Forward declarations
    class ContextWrapper;
    class OperationWrapper;
    class ValueWrapper;
    class ServerWrapper;
    class SharedPVWrapper;
    class StaticSourceWrapper;
    class MonitorWrapper;
    class MonitorBuilderWrapper;

    /// Exception wrapper for Rust-friendly error handling
    class PvxsError : public std::runtime_error
    {
    public:
        explicit PvxsError(const std::string &msg) : std::runtime_error(msg) {}
    };

    /// Exception for monitor connection events (when maskConnected(true))
    class MonitorConnected : public PvxsError
    {
    public:
        explicit MonitorConnected(const std::string &msg) : PvxsError(msg) {}
    };

    /// Exception for monitor disconnection events (when maskDisconnected(true))
    class MonitorDisconnected : public PvxsError
    {
    public:
        explicit MonitorDisconnected(const std::string &msg) : PvxsError(msg) {}
    };

    /// Exception for monitor finished events (when maskDisconnected(true))
    class MonitorFinished : public PvxsError
    {
    public:
        explicit MonitorFinished(const std::string &msg) : PvxsError(msg) {}
    };

    /// Exception for monitor remote error events from server
    class MonitorRemoteError : public PvxsError
    {
    public:
        explicit MonitorRemoteError(const std::string &msg) : PvxsError(msg) {}
    };

    /// Exception for monitor client side errors
    class MonitorClientError : public PvxsError
    {
    public:
        explicit MonitorClientError(const std::string &msg) : PvxsError(msg) {}
    };

    /// Wraps pvxs::Value for safe Rust access
    class ValueWrapper
    {
    private:
        pvxs::Value value_;

    public:
        ValueWrapper() = default;
        explicit ValueWrapper(pvxs::Value &&val) : value_(std::move(val)) {}

        // Check if value is valid
        bool valid() const { return value_.valid(); }

        // Get field as string (simplified for now)
        std::string get_field_string(const std::string &field_name) const;

        // Get field as double
        double get_field_double(const std::string &field_name) const;

        // Get field as int32
        int32_t get_field_int32(const std::string &field_name) const;

        // Get field as enum (int16)
        std::int16_t get_field_enum(const std::string &field_name) const;

        // Get field as array of doubles
        rust::Vec<double> get_field_double_array(const std::string &field_name) const;

        // Get field as array of int32
        rust::Vec<int32_t> get_field_int32_array(const std::string &field_name) const;

        // Get field as array of enums (int16)
        rust::Vec<int16_t> get_field_enum_array(const std::string &field_name) const;

        // Get field as array of strings
        rust::Vec<rust::String> get_field_string_array(const std::string &field_name) const;

        // Convert entire value to string representation
        std::string to_string() const;

        // Get the underlying pvxs::Value (internal use)
        pvxs::Value &get() { return value_; }
        const pvxs::Value &get() const { return value_; }
    };

    /// Wraps pvxs::client::Operation for safe Rust access
    class OperationWrapper
    {
    private:
        std::shared_ptr<pvxs::client::Operation> op_;

    public:
        OperationWrapper() = default;
        explicit OperationWrapper(std::shared_ptr<pvxs::client::Operation> &&op)
            : op_(std::move(op)) {}

        // Wait for operation to complete (blocking)
        std::unique_ptr<ValueWrapper> wait(double timeout) const;

        // Cancel the operation
        bool cancel() const;

        // Check if operation is complete
        bool is_done() const;

        // Get result (non-blocking, throws if not ready)
        std::unique_ptr<ValueWrapper> get_result();

        // Wait for completion with timeout (returns true if completed)
        bool wait_for_completion(uint64_t timeout_ms);

        // Get PV name
        std::string name() const;
    };

    /// Wraps pvxs::client::Subscription for safe Rust access
    class MonitorWrapper
    {
    private:
        pvxs::client::Context &context_;
        std::shared_ptr<pvxs::client::Subscription> monitor_;
        std::shared_ptr<pvxs::client::Connect> connect_;  // Track connection state
        std::string pv_name_;
        void (*rust_callback_)() = nullptr;  // Function pointer to Rust callback (no parameters)
        bool mask_connected_ = true;  // Default masks (filter out connection events)
        bool mask_disconnected_ = true;  // Default masks (filter out disconnection events)

    public:
        MonitorWrapper() = delete; // Must have context and PV name
        MonitorWrapper(pvxs::client::Context &ctx, const std::string &pv_name)
            : context_(ctx), pv_name_(pv_name) {}
        explicit MonitorWrapper(std::shared_ptr<pvxs::client::Subscription> &&monitor, const std::string &pv_name, pvxs::client::Context &ctx)
            : context_(ctx), monitor_(std::move(monitor)), pv_name_(pv_name) {}
        explicit MonitorWrapper(std::shared_ptr<pvxs::client::Subscription> &&monitor, const std::string &pv_name, pvxs::client::Context &ctx, void (*callback)())
            : context_(ctx), monitor_(std::move(monitor)), pv_name_(pv_name), rust_callback_(callback) {}
        // Constructor with mask settings
        explicit MonitorWrapper(std::shared_ptr<pvxs::client::Subscription> &&monitor, const std::string &pv_name, pvxs::client::Context &ctx, void (*callback)(), bool mask_connected, bool mask_disconnected)
            : context_(ctx), monitor_(std::move(monitor)), pv_name_(pv_name), rust_callback_(callback), mask_connected_(mask_connected), mask_disconnected_(mask_disconnected) {}

        // Start monitoring
        void start();

        // Stop monitoring
        void stop();

        // Check if monitor is running
        bool is_running() const;

        // Check if there are updates available (non-blocking)
        bool has_update() const;

        // Get the next update (blocking with timeout)
        std::unique_ptr<ValueWrapper> get_update(double timeout);

        // Get the next update (non-blocking, returns nullptr if no update)
        std::unique_ptr<ValueWrapper> try_get_update();

        // Get PV name
        std::string name() const { return pv_name_; }

        // Get connection status
        bool is_connected() const;
        
        // Set the Connect object (used by MonitorBuilder)
        void set_connect(std::shared_ptr<pvxs::client::Connect> &&connect) {
            connect_ = std::move(connect);
        }
        
        // Pop next value from subscription queue (PVXS-style)
        std::unique_ptr<ValueWrapper> pop();
    };

    /// Builder pattern for creating monitors with callbacks (PVXS-style)
    class MonitorBuilderWrapper
    {
    private:
        pvxs::client::Context &context_;
        std::string pv_name_;
        bool mask_connected_ = false;  // Default: false = don't suppress connection events (per PVXS default)
        bool mask_disconnected_ = true; // Default: true = suppress disconnection events (per PVXS default)
        uint64_t callback_id_ = 0;
        void (*rust_callback_)() = nullptr;  // Function pointer to Rust callback (no parameters)

    public:
        MonitorBuilderWrapper() = delete;
        MonitorBuilderWrapper(pvxs::client::Context &ctx, const std::string &pv_name)
            : context_(ctx), pv_name_(pv_name) {}

        // Configure whether to include Connected events in queue
        void mask_connected(bool mask);
        
        // Configure whether to include Disconnected events in queue  
        void mask_disconnected(bool mask);
        
        // Set event callback function pointer
        void set_event_callback(void (*callback)());
        
        // Execute and return a subscription without callback
        std::unique_ptr<MonitorWrapper> exec();
        
        // Execute and return a subscription with event callback
        std::unique_ptr<MonitorWrapper> exec_with_callback(uint64_t callback_id);
        
        // Get PV name
        std::string name() const { return pv_name_; }
    };

    /// Wraps pvxs::client::Context for safe Rust access
    class ContextWrapper
    {
    private:
        pvxs::client::Context context_;

    public:
        // Create context from environment variables
        static std::unique_ptr<ContextWrapper> from_env();

        // Create context with explicit configuration
        explicit ContextWrapper(pvxs::client::Context &&ctx)
            : context_(std::move(ctx)) {}

        // Perform a GET operation (synchronous version for simplicity)
        std::unique_ptr<ValueWrapper> get(const std::string &pv_name, double timeout);

        // Start an async GET operation
        std::unique_ptr<OperationWrapper> get_async(const std::string &pv_name, double timeout);

        // Start an async PUT operation
        std::unique_ptr<OperationWrapper> put_double_async(const std::string &pv_name, double value, double timeout);

        // Start an async INFO operation
        std::unique_ptr<OperationWrapper> info_async(const std::string &pv_name, double timeout);

        // Perform a PUT operation (simplified - just set a double value)
        void put(const std::string &pv_name, double value, double timeout);

        // Perform a PUT operation (simplified - just set an int32 value)
        void put(const std::string &pv_name, int32_t value, double timeout);

        // Perform a PUT operation (simplified - just set a string value)
        void put(const std::string &pv_name, const std::string &value, double timeout);

        // Perform a PUT operation (simplified - just set an enum value)
        void put(const std::string &pv_name, int16_t value, double timeout);

        // Perform a PUT operation (simplified - just set a double array)
        void put(const std::string &pv_name, const rust::Vec<double> &value, double timeout);

        // Perform a PUT operation (simplified - just set an int32 array)
        void put(const std::string &pv_name, const rust::Vec<int32_t> &value, double timeout);

        // Perform a PUT operation (simplified - just set an enum array)
        void put(const std::string &pv_name, const rust::Vec<int16_t> &value, double timeout);

        // Perform a PUT operation (simplified - just set a string array)
        void put(const std::string &pv_name, const rust::Vec<rust::String> &value, double timeout);

        // Get type information (INFO operation)
        std::unique_ptr<ValueWrapper> info(const std::string &pv_name, double timeout);

        // Create RPC builder
        std::unique_ptr<class RpcWrapper> rpc_create(const std::string &pv_name);

        // Create Monitor
        std::unique_ptr<MonitorWrapper> monitor(const std::string &pv_name);
        
        // Create MonitorBuilder (PVXS-style)
        std::unique_ptr<MonitorBuilderWrapper> monitor_builder(const std::string &pv_name);
    };

    /// Wraps RPC operations for safe Rust access
    class RpcWrapper
    {
    private:
        pvxs::client::Context &context_;
        std::string pv_name_;
        pvxs::Value arguments_;

    public:
        RpcWrapper(pvxs::client::Context &ctx, const std::string &pv_name)
            : context_(ctx), pv_name_(pv_name) {}

        // Add arguments to the RPC call
        void arg_string(const std::string &name, const std::string &value);
        void arg_double(const std::string &name, double value);
        void arg_int32(const std::string &name, int32_t value);
        void arg_bool(const std::string &name, bool value);

        // Execute RPC call synchronously
        std::unique_ptr<ValueWrapper> execute_sync(double timeout);

        // Execute RPC call asynchronously
        std::unique_ptr<OperationWrapper> execute_async(double timeout);
    };

    // Factory functions for Rust (these will be exposed via cxx bridge)
    std::unique_ptr<ContextWrapper> create_context_from_env();

    // RPC operations bridge functions
    std::unique_ptr<RpcWrapper> context_rpc_create(
        ContextWrapper &ctx,
        rust::String pv_name);

    void rpc_arg_string(RpcWrapper &rpc, rust::String name, rust::String value);
    void rpc_arg_double(RpcWrapper &rpc, rust::String name, double value);
    void rpc_arg_int32(RpcWrapper &rpc, rust::String name, int32_t value);
    void rpc_arg_bool(RpcWrapper &rpc, rust::String name, bool value);

    std::unique_ptr<ValueWrapper> rpc_execute_sync(RpcWrapper &rpc, double timeout);
    std::unique_ptr<OperationWrapper> rpc_execute_async(RpcWrapper &rpc, double timeout);
    std::unique_ptr<ValueWrapper> context_get(ContextWrapper &ctx, rust::Str pv_name, double timeout);
    void context_put_double(ContextWrapper &ctx, rust::Str pv_name, double value, double timeout);
    void context_put_int32(ContextWrapper &ctx, rust::Str pv_name, int32_t value, double timeout);
    void context_put_string(ContextWrapper &ctx, rust::Str pv_name, rust::String value, double timeout);
    void context_put_enum(ContextWrapper &ctx, rust::Str pv_name, int16_t value, double timeout);
    void context_put_double_array(ContextWrapper &ctx, rust::Str pv_name, rust::Vec<double> value, double timeout);
    void context_put_int32_array(ContextWrapper &ctx, rust::Str pv_name, rust::Vec<int32_t> value, double timeout);
    void context_put_string_array(ContextWrapper &ctx, rust::Str pv_name, rust::Vec<int16_t> value, double timeout);
    void context_put_string_array(ContextWrapper &ctx, rust::Str pv_name, rust::Vec<int16_t> value, double timeout);
    void context_put_string_array(ContextWrapper &ctx, rust::Str pv_name, rust::Vec<rust::String> value, double timeout);
    std::unique_ptr<ValueWrapper> context_info(ContextWrapper &ctx, rust::Str pv_name, double timeout);
    // ============================================================================
    // Async operations for Rust
    std::unique_ptr<OperationWrapper> context_get_async(ContextWrapper &ctx, rust::Str pv_name, double timeout);
    std::unique_ptr<OperationWrapper> context_put_double_async(ContextWrapper &ctx, rust::Str pv_name, double value, double timeout);
    std::unique_ptr<OperationWrapper> context_info_async(ContextWrapper &ctx, rust::Str pv_name, double timeout);

    // Operation management for Rust
    bool operation_is_done(const OperationWrapper &op);
    std::unique_ptr<ValueWrapper> operation_get_result(OperationWrapper &op);
    void operation_cancel(OperationWrapper &op);
    bool operation_wait_for_completion(OperationWrapper &op, uint64_t timeout_ms);

    // Value accessors for Rust
    bool value_is_valid(const ValueWrapper &val);
    rust::String value_to_string(const ValueWrapper &val);
    double value_get_field_double(const ValueWrapper &val, rust::String field_name);
    int32_t value_get_field_int32(const ValueWrapper &val, rust::String field_name);
    rust::String value_get_field_string(const ValueWrapper &val, rust::String field_name);
    int16_t value_get_field_enum(const ValueWrapper &val, rust::String field_name);
    rust::Vec<double> value_get_field_double_array(const ValueWrapper &val, rust::String field_name);
    rust::Vec<int32_t> value_get_field_int32_array(const ValueWrapper &val, rust::String field_name);
    rust::Vec<rust::String> value_get_field_string_array(const ValueWrapper &val, rust::String field_name);

    // Monitor operations for Rust
    std::unique_ptr<MonitorWrapper> context_monitor_create(ContextWrapper &ctx, rust::String pv_name);
    void monitor_start(MonitorWrapper &monitor);
    void monitor_stop(MonitorWrapper &monitor);
    bool monitor_is_running(const MonitorWrapper &monitor);
    bool monitor_has_update(const MonitorWrapper &monitor);
    std::unique_ptr<ValueWrapper> monitor_get_update(MonitorWrapper &monitor, double timeout);
    std::unique_ptr<ValueWrapper> monitor_try_get_update(MonitorWrapper &monitor);
    bool monitor_is_connected(const MonitorWrapper &monitor);
    rust::String monitor_get_name(const MonitorWrapper &monitor);
    std::unique_ptr<ValueWrapper> monitor_pop(MonitorWrapper &monitor);

    // MonitorBuilder operations for Rust
    std::unique_ptr<MonitorBuilderWrapper> context_monitor_builder_create(ContextWrapper &ctx, rust::String pv_name);
    void monitor_builder_mask_connected(MonitorBuilderWrapper &builder, bool mask);
    void monitor_builder_mask_disconnected(MonitorBuilderWrapper &builder, bool mask);
    void monitor_builder_set_event_callback(MonitorBuilderWrapper &builder, uintptr_t callback_ptr);
    std::unique_ptr<MonitorWrapper> monitor_builder_exec(MonitorBuilderWrapper &builder);
    std::unique_ptr<MonitorWrapper> monitor_builder_exec_with_callback(MonitorBuilderWrapper &builder, uint64_t callback_id);

    // ============================================================================
    // Server-side wrappers
    // ============================================================================

    /// Wraps pvxs::server::SharedPV for safe Rust access
    class SharedPVWrapper
    {
    private:
        pvxs::server::SharedPV pv_;
        pvxs::Value template_value_; // Store template for cloneEmpty()

    public:
        SharedPVWrapper() = default;
        explicit SharedPVWrapper(pvxs::server::SharedPV &&pv) : pv_(std::move(pv)) {}

        // Open the PV with initial value
        void open(const ValueWrapper &initial_value);

        // Check if PV is open
        bool is_open() const;

        // Close the PV
        void close();

        // Post a new value
        void post_value(const ValueWrapper &value);

        // Get current value
        std::unique_ptr<ValueWrapper> fetch_value() const;

        // Get the underlying SharedPV (internal use)
        pvxs::server::SharedPV &get() { return pv_; }
        const pvxs::server::SharedPV &get() const { return pv_; }

        // Get template value for creating compatible updates
        const pvxs::Value &get_template() const { return template_value_; }

        // Factory methods
        static std::unique_ptr<SharedPVWrapper> create_mailbox();
        static std::unique_ptr<SharedPVWrapper> create_readonly();
    };

    /// Wraps pvxs::server::StaticSource for safe Rust access
    class StaticSourceWrapper
    {
    private:
        pvxs::server::StaticSource source_;

    public:
        StaticSourceWrapper() = default;
        explicit StaticSourceWrapper(pvxs::server::StaticSource source) : source_(std::move(source)) {}

        // Add a SharedPV with a name
        void add_pv(const std::string &name, SharedPVWrapper &pv);

        // Remove a PV by name
        void remove_pv(const std::string &name);

        // Close all PVs
        void close_all();

        // Get the underlying source (internal use)
        pvxs::server::StaticSource &get() { return source_; }
        const pvxs::server::StaticSource &get() const { return source_; }

        // Factory method
        static std::unique_ptr<StaticSourceWrapper> create();
    };

    /// Wraps pvxs::server::Server for safe Rust access
    class ServerWrapper
    {
    private:
        pvxs::server::Server server_;

    public:
        ServerWrapper() = default;
        explicit ServerWrapper(pvxs::server::Server &&server) : server_(std::move(server)) {}

        // Start the server
        void start();

        // Stop the server
        void stop();

        // Add a SharedPV directly (uses built-in StaticSource)
        void add_pv(const std::string &name, SharedPVWrapper &pv);

        // Remove a PV by name
        void remove_pv(const std::string &name);

        // Add a source
        void add_source(const std::string &name, StaticSourceWrapper &source, int order);

        // Get server configuration info
        uint16_t get_tcp_port() const;
        uint16_t get_udp_port() const;

        // Factory methods
        static std::unique_ptr<ServerWrapper> from_env();
        static std::unique_ptr<ServerWrapper> isolated();
    };

    // ============================================================================
    // Server factory functions for Rust FFI
    // ============================================================================

    // Server creation
    std::unique_ptr<ServerWrapper> server_create_from_env();
    std::unique_ptr<ServerWrapper> server_create_isolated();

    // Server operations
    void server_start(ServerWrapper &server);
    void server_stop(ServerWrapper &server);
    void server_add_pv(ServerWrapper &server, rust::String name, SharedPVWrapper &pv);
    void server_remove_pv(ServerWrapper &server, rust::String name);
    void server_add_source(ServerWrapper &server, rust::String name, StaticSourceWrapper &source, int32_t order);
    uint16_t server_get_tcp_port(const ServerWrapper &server);
    uint16_t server_get_udp_port(const ServerWrapper &server);

    // SharedPV creation and operations
    std::unique_ptr<SharedPVWrapper> shared_pv_create_mailbox();
    std::unique_ptr<SharedPVWrapper> shared_pv_create_readonly();
    
    // NTScalar metadata structures with C++ optional for optional fields
    struct NTScalarAlarm {
        int32_t severity;
        int32_t status;
        rust::String message;
    };
    
    struct NTScalarTime {
        int64_t seconds_past_epoch;
        int32_t nanoseconds;
        int32_t user_tag;
    };
    
    struct NTScalarDisplay {
        int64_t limit_low;
        int64_t limit_high;
        rust::String description;
        rust::String units;
        int32_t precision;
    };
    
    struct NTScalarControl {
        double limit_low;
        double limit_high;
        double min_step;
    };
    
    struct NTScalarValueAlarm {
        bool active;
        double low_alarm_limit;
        double low_warning_limit;
        double high_warning_limit;
        double high_alarm_limit;
        int32_t low_alarm_severity;
        int32_t low_warning_severity;
        int32_t high_warning_severity;
        int32_t high_alarm_severity;
        uint8_t hysteresis;
    };
    
    struct NTScalarMetadata {
        NTScalarAlarm alarm;
        NTScalarTime time_stamp;
        std::optional<NTScalarDisplay> display;
        std::optional<NTScalarControl> control;
        std::optional<NTScalarValueAlarm> value_alarm;
    };

    struct NTEnumMetadata {
        NTScalarAlarm alarm;
        NTScalarTime time_stamp;
    };
    
    // Builder functions for metadata construction from Rust
    std::unique_ptr<NTScalarAlarm> create_alarm(int32_t severity, int32_t status, rust::String message);
    std::unique_ptr<NTScalarTime> create_time(int64_t seconds_past_epoch, int32_t nanoseconds, int32_t user_tag);
    std::unique_ptr<NTScalarDisplay> create_display(int64_t limit_low, int64_t limit_high, rust::String description, rust::String units, int32_t precision);
    std::unique_ptr<NTScalarControl> create_control(double limit_low, double limit_high, double min_step);
    std::unique_ptr<NTScalarValueAlarm> create_value_alarm(bool active, double low_alarm_limit, double low_warning_limit, 
                                                            double high_warning_limit, double high_alarm_limit,
                                                            int32_t low_alarm_severity, int32_t low_warning_severity,
                                                            int32_t high_warning_severity, int32_t high_alarm_severity, uint8_t hysteresis);
    
    // Helper functions to build metadata with different combinations of optional fields
    std::unique_ptr<NTScalarMetadata> create_metadata_no_optional(const NTScalarAlarm& alarm, const NTScalarTime& time_stamp);
    std::unique_ptr<NTScalarMetadata> create_metadata_with_display(const NTScalarAlarm& alarm, const NTScalarTime& time_stamp, const NTScalarDisplay& display);
    std::unique_ptr<NTScalarMetadata> create_metadata_with_control(const NTScalarAlarm& alarm, const NTScalarTime& time_stamp, const NTScalarControl& control);
    std::unique_ptr<NTScalarMetadata> create_metadata_with_value_alarm(const NTScalarAlarm& alarm, const NTScalarTime& time_stamp, const NTScalarValueAlarm& value_alarm);
    std::unique_ptr<NTScalarMetadata> create_metadata_with_display_control(const NTScalarAlarm& alarm, const NTScalarTime& time_stamp, const NTScalarDisplay& display, const NTScalarControl& control);
    std::unique_ptr<NTScalarMetadata> create_metadata_with_display_value_alarm(const NTScalarAlarm& alarm, const NTScalarTime& time_stamp, const NTScalarDisplay& display, const NTScalarValueAlarm& value_alarm);
    std::unique_ptr<NTScalarMetadata> create_metadata_with_control_value_alarm(const NTScalarAlarm& alarm, const NTScalarTime& time_stamp, const NTScalarControl& control, const NTScalarValueAlarm& value_alarm);
    std::unique_ptr<NTScalarMetadata> create_metadata_full(const NTScalarAlarm& alarm, const NTScalarTime& time_stamp, const NTScalarDisplay& display, const NTScalarControl& control, const NTScalarValueAlarm& value_alarm);
    
    std::unique_ptr<NTEnumMetadata> create_enum_metadata(const NTScalarAlarm& alarm, const NTScalarTime& time_stamp);

    void shared_pv_open_double(SharedPVWrapper& pv, double initial_value, const NTScalarMetadata& metadata);
    void shared_pv_open_double_array(SharedPVWrapper& pv, rust::Vec<double> initial_value, const NTScalarMetadata& metadata);
    void shared_pv_open_int32(SharedPVWrapper &pv, int32_t initial_value, const NTScalarMetadata& metadata);
    void shared_pv_open_int32_array(SharedPVWrapper& pv, rust::Vec<int32_t> initial_value, const NTScalarMetadata& metadata);
    void shared_pv_open_string(SharedPVWrapper& pv, rust::String initial_value, const NTScalarMetadata& metadata);
    void shared_pv_open_string_array(SharedPVWrapper& pv, rust::Vec<rust::String> initial_value, const NTScalarMetadata& metadata);
    void shared_pv_open_enum(SharedPVWrapper &pv, rust::Vec<rust::String> enum_choices, int16_t selected_choice, const NTEnumMetadata& metadata);
    bool shared_pv_is_open(const SharedPVWrapper &pv);
    void shared_pv_close(SharedPVWrapper &pv);
    void shared_pv_post_double(SharedPVWrapper &pv, double value);
    void shared_pv_post_double_with_alarm(SharedPVWrapper &pv, double value, int32_t severity, int32_t status, rust::String message);
    void shared_pv_post_int32(SharedPVWrapper &pv, int32_t value);
    void shared_pv_post_int32_with_alarm(SharedPVWrapper &pv, int32_t value, int32_t severity, int32_t status, rust::String message);
    void shared_pv_post_string(SharedPVWrapper &pv, rust::String value);
    void shared_pv_post_enum(SharedPVWrapper &pv, int16_t value);    void shared_pv_post_enum_with_alarm(SharedPVWrapper &pv, int16_t value, int32_t severity, int32_t status, rust::String message);    void shared_pv_post_double_array(SharedPVWrapper &pv, rust::Vec<double> value);
    void shared_pv_post_int32_array(SharedPVWrapper &pv, rust::Vec<int32_t> value);
    void shared_pv_post_string_array(SharedPVWrapper &pv, rust::Vec<rust::String> value);
    std::unique_ptr<ValueWrapper> shared_pv_fetch(const SharedPVWrapper &pv);

    // StaticSource creation and operations
    std::unique_ptr<StaticSourceWrapper> static_source_create();
    void static_source_add_pv(StaticSourceWrapper &source, rust::String name, SharedPVWrapper &pv);
    void static_source_remove_pv(StaticSourceWrapper &source, rust::String name);
    void static_source_close_all(StaticSourceWrapper &source);

    // ============================================================================
    // Logging control
    
    /// Configure PVXS logging from environment variable PVXS_LOG
    void pvxs_logger_config_env();
    
    /// Set logging level for a specific logger
    /// @param name Logger name (supports wildcards like "pvxs.*")
    /// @param level One of: "CRIT", "ERR", "WARN", "INFO", "DEBUG"
    void pvxs_logger_level_set(rust::String name, rust::String level);

    // ============================================================================
    // Note: RPC Source implementation - to be added later when needed

} // namespace pvxs_wrapper