Examples

Example are built, but not installed. They can be found under example/O.*

Latest versions https://github.com/mdavidsaver/pvxs/blob/master/example/

Shortest Client Get

/**
 * Copyright - See the COPYRIGHT that is included with this distribution.
 * pvxs is distributed subject to a Software License Agreement found
 * in file LICENSE that is included with this distribution.
 */
/** The short example of a client GET operation.
 */

#include <iostream>

#include <pvxs/client.h>
#include <pvxs/log.h>

int main(int argc, char* argv[]) {
    using namespace pvxs;

    // (Optional) configuring logging using $PVXS_LOG
    logger_config_env();

    // Configure client using $EPICS_PVA_*
    auto ctxt(client::Context::fromEnv());

    // fetch PV "some:pv:name" and wait up to 5 seconds for a reply.
    // (throws an exception on error, including timeout)
    Value reply = ctxt.get("some:pv:name").exec()->wait(5.0);

    // Reply is printed to stdout.
    std::cout<<reply;

    return 0;
}

Shortest Server

/**
 * Copyright - See the COPYRIGHT that is included with this distribution.
 * pvxs is distributed subject to a Software License Agreement found
 * in file LICENSE that is included with this distribution.
 */
/** The short example of a server.
 */

#include <iostream>

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

int main(int argc, char* argv[]) {
    using namespace pvxs;

    // (Optional) configuring logging using $PVXS_LOG
    logger_config_env();

    // Use pre-defined NTScalar structure w/ double for primary value field.
    Value initial = nt::NTScalar{TypeCode::Float64}.create();
    initial["value"] = 42.0;

    // Storage and access for network visible Process Variable
    server::SharedPV pv(server::SharedPV::buildMailbox());
    pv.open(initial);

    server::Server::fromEnv()        // Configure a server using $EPICS_PVAS_* or $EPICS_PVA_*
            .addPV("my:pv:name", pv) // add (and name) one local PV
            .run();                  // run until SIGINT

    return 0;
}

Client Demo

/**
 * Copyright - See the COPYRIGHT that is included with this distribution.
 * pvxs is distributed subject to a Software License Agreement found
 * in file LICENSE that is included with this distribution.
 */
/** Meant to be run against the mailbox example.
 *  eg. in one terminal run:
 *
 *   ./mailbox some:pv:name
 *
 * And in another terminal run:
 *
 *   ./client some:pv:name
 */

#include <iostream>

#include <pvxs/client.h>
#include <pvxs/log.h>

using namespace pvxs;

int main(int argc, char* argv[])
{
    if(argc<2) {
        std::cerr<<"Usage: "<<argv[0]<<" <pvname>\n";
        return 1;
    }

    // Read $PVXS_LOG from process environment and update
    // logging configuration.  eg.
    //    export PVXS_LOG=*=DEBUG
    // makes a lot of noise.
    logger_config_env();

    // Create a client context
    auto ctxt(client::Context::fromEnv());

    // Fetch current value
    int32_t current;
    {
        std::cout<<"Getting current value of '"<<argv[1]<<"'"<<std::endl;
        // Build and start network operation
        auto op = ctxt.get(argv[1])
                .exec();

        // wait for it to complete, for up to 5 seconds.
        Value result = op->wait(5.0);

        std::cout<<"Result is:\n"<<result<<std::endl;

        if(auto value = result["value"]) {
            // there is a .value field
            // as may still throw pvxs::NoConvert if its value can't
            // be converted to int32_t
            current = value.as<int32_t>();
        } else {
            // an example.  won't happen with mailbox server
            std::cerr<<"Server type doesn't have .value field!\n";
            return 1;
        }
    }

    {
        // attempt to change.
        // uses simple builder form to assign .value

        ctxt.put(argv[1])
                .set("value", current+1)
                .exec()
                ->wait(5.0);

        std::cout<<"First increment successful"<<std::endl;
    }

    {
        // change again.
        // use build() callback

        auto op = ctxt.put(argv[1])
                // provide present value to build() callback.
                .fetchPresent(true)
                .build([](Value&& current) -> Value {
                    // allocate an empty container
                    auto toput(current.cloneEmpty());

                    // fill in .value.
                    // Assignment implicitly marks .value as changed
                    toput["value"] = current["value"].as<int32_t>() + 1;

                    // return the container to be sent
                    return toput;
                })
                .exec();

        op->wait(5.0);

        std::cout<<"Second increment successful"<<std::endl;
    }

    // fetch final value
    {
        std::cout<<"Getting current value of '"<<argv[1]<<"'"<<std::endl;
        // Build and start network operation
        auto result = ctxt.get(argv[1])
                .exec()
                ->wait(5.0);

        std::cout<<"Result is:\n"<<result<<std::endl;
    }

    return 0;
}

Mailbox Server

/**
 * Copyright - See the COPYRIGHT that is included with this distribution.
 * pvxs is distributed subject to a Software License Agreement found
 * in file LICENSE that is included with this distribution.
 */
/**
 * Serves a single PV name.
 * Any updates written (PUT) to this PV will be stored verbatim
 * and sent to any subscribers.
 */

#include <iostream>

#include <epicsTime.h>

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

using namespace pvxs;

int main(int argc, char* argv[])
{
    if(argc<=1) {
        std::cerr<<"Usage: "<<argv[0]<<" <pvname> [pvname1 [...]]\n";
        return 1;
    }

    // Read $PVXS_LOG from process environment and update
    // logging configuration.  eg.
    //    export PVXS_LOG=*=DEBUG
    // makes a lot of noise.
    logger_config_env();

    // Must provide a data type for the mailbox.
    // Use pre-canned definition of scalar with meta-data
    Value initial = nt::NTScalar{TypeCode::Float64}.create();

    // (optional) Provide an initial value
    initial["value"] = 42.0;
    initial["alarm.severity"] = 0;
    initial["alarm.status"] = 0;
    initial["alarm.message"] = "";

    std::vector<server::SharedPV> pvs(argc-1u);

    for(size_t i=0ul; i<pvs.size(); i++) {
        // Actually creating a mailbox PV.
        // buildMailbox() installs a default onPut() handler which
        // stores whatever a client sends (subject to our data type).
        server::SharedPV pv(server::SharedPV::buildMailbox());

        // (optional) Replace the default PUT handler to do a range check
        pv.onPut([](server::SharedPV& pv,
                 std::unique_ptr<server::ExecOp>&& op,
                 Value&& top)
         {

             // (optional) arbitrarily clip value to [-100.0, 100.0]
             double val(top["value"].as<double>());
             if(val<-100.0)
                 top["value"] = -100.0;
             else if(val>100.0)
                 top["value"] = 100.0;

             // (optional) Provide a timestamp if the client has not (common)
             Value ts(top["timeStamp"]);
             if(!ts.isMarked(true, true)) {
                 // use current time
                 epicsTimeStamp now;
                 if(!epicsTimeGetCurrent(&now)) {
                     ts["secondsPastEpoch"] = now.secPastEpoch + POSIX_TIME_AT_EPICS_EPOCH;
                     ts["nanoseconds"] = now.nsec;
                 }
             }

             // (optional) update the SharedPV cache and send
             // a update to any subscribers
             pv.post(top);

             // Required.  Inform client that PUT operation is complete.
             op->reply();
         });

        // Associate a data type (and maybe initial value) with this PV
        pv.open(initial);

        pvs[i] = std::move(pv);
    }

    // Build server which will serve this PV
    // Configure using process environment.
    auto serv(server::Server::fromEnv());

    for(size_t i=0ul; i<pvs.size(); i++) {
        serv.addPV(argv[i+1], pvs[i]);
    }

    // (optional) Print the configuration this server is using
    // with any auto-address list expanded.
    std::cout<<"Effective config\n"<<serv.config();

    std::cout<<"Running\n";

    // Start server and run forever, or until Ctrl+c is pressed.
    // Returns on SIGINT or SIGTERM
    serv.run();

    std::cout<<"Done\n";

    return 0;
}