static void setValue(PVUnionPtr const &pvUnion, double value)
{
    UnionConstPtr u = pvUnion->getUnion();
    FieldConstPtr field = u->getField(0);
    Type type = field->getType();
    if(type==scalar) {
         ScalarConstPtr scalar = static_pointer_cast<const Scalar>(field);
         ScalarType scalarType = scalar->getScalarType();
         if(scalarType==pvDouble) {
              PVDoublePtr pvValue = static_pointer_cast<PVDouble>(
                   pvDataCreate->createPVScalar(pvDouble));
              pvValue->put(value);
              pvUnion->set(0,pvValue);
              return;
         }
         if(scalarType==pvString) {
              PVStringPtr pvValue = static_pointer_cast<PVString>(
                   pvDataCreate->createPVScalar(pvString));
              stringstream ss;
              ss << "value" << value;
              pvValue->put(ss.str());
              pvUnion->set(0,pvValue);
              return;
         }
         throw std::runtime_error("only pvDouble and pvString are supported");
    }
    if(type==scalarArray) {
         ScalarArrayConstPtr scalarArray = static_pointer_cast<const ScalarArray>(field);
         ScalarType scalarType = scalarArray->getElementType();
         if(scalarType==pvDouble) {
              size_t num = 5;
              PVDoubleArrayPtr pvValue = static_pointer_cast<PVDoubleArray>(
                   pvDataCreate->createPVScalarArray(pvDouble));
              shared_vector<double> data(num);
              for(size_t i=0; i<num; ++i) data[i] = value +i;
              pvValue->replace(freeze(data));
              pvUnion->set(0,pvValue);
              return;
         }
         if(scalarType==pvString) {
              size_t num = 5;
              PVStringArrayPtr pvValue = static_pointer_cast<PVStringArray>(
                   pvDataCreate->createPVScalarArray(pvString));
              shared_vector<string> data(num);
              for(size_t i=0; i<num; ++i) {
                  stringstream ss;
                  ss << "value" << value << i;
                  data[i] = ss.str();
              }
              pvValue->replace(freeze(data));
              pvUnion->set(0,pvValue);
              return;
         }
         throw std::runtime_error("only pvDouble and pvString are supported");
    }
    throw std::runtime_error("only scalar and scalarArray fields are supported");
}
void PvaClientPutData::putDoubleArray(shared_vector<const double> const & value)
{
    checkValue();
    PVDoubleArrayPtr pv = pvStructure->getSubField<PVDoubleArray>("value");
    if(!pv) {
        throw std::runtime_error(messagePrefix + notDoubleArray);
    }
    pv->replace(value);
}
void test_ntcontinuum()
{
    testDiag("test_ntcontinuum");

    NTContinuumBuilderPtr builder = NTContinuum::createBuilder();
    testOk(builder.get() != 0, "Got builder");

    NTContinuumPtr ntContinuum = builder->
            addDescriptor()->
            addAlarm()->
            addTimeStamp()->
            add("extra1",fieldCreate->createScalar(pvString)) ->
            add("extra2",fieldCreate->createScalarArray(pvString)) ->
            create();
    testOk1(ntContinuum.get() != 0);

    testOk1(ntContinuum->getPVStructure().get() != 0);
    testOk1(ntContinuum->getDescriptor().get() != 0);
    testOk1(ntContinuum->getAlarm().get() != 0);
    testOk1(ntContinuum->getTimeStamp().get() != 0);
    testOk1(ntContinuum->getBase().get() != 0);
    testOk1(ntContinuum->getValue().get() != 0);
    //
    // example how to set base
    //
    PVDoubleArray::svector newBase;
    newBase.push_back(1.0);
    newBase.push_back(2.0);

    PVDoubleArrayPtr pvBaseField = ntContinuum->getBase();
    pvBaseField->replace(freeze(newBase));

    //
    // example how to get bases
    //
    PVDoubleArray::const_svector base(pvBaseField->view());

    testOk1(base.size() == 2);
    testOk1(base[0] == 1.0);
    testOk1(base[1] == 2.0);

    //
    // example how to set values
    //
    PVDoubleArray::svector newValue;
    newValue.push_back(1.0);
    newValue.push_back(2.0);
    newValue.push_back(10.0);
    newValue.push_back(20.0);
    newValue.push_back(100.0);
    newValue.push_back(200.0);

    PVDoubleArrayPtr pvValueField = ntContinuum->getValue();
    pvValueField->replace(freeze(newValue));

    //
    // example how to get values
    //
    PVDoubleArray::const_svector value(pvValueField->view());

    testOk1(value.size() == 6);
    testOk1(value[0] == 1.0);
    testOk1(value[1] == 2.0);
    testOk1(value[2] == 10.0);
    testOk1(value[3] == 20.0);
    testOk1(value[4] == 100.0);
    testOk1(value[5] == 200.0);

    //
    // example how to set units
    //
    PVStringArray::svector newUnits;
    newUnits.push_back("s");
    newUnits.push_back("ms");
    newUnits.push_back("us");
    newUnits.push_back("s");

    PVStringArrayPtr pvUnitsField = ntContinuum->getUnits();
    pvUnitsField->replace(freeze(newUnits));

    //
    // example how to get units
    //
    PVStringArray::const_svector units(pvUnitsField->view());

    testOk1(units.size() == 4);
    testOk1(units[0] == "s");
    testOk1(units[1] == "ms");
    testOk1(units[2] == "us");
    testOk1(units[3] == "s");

    //
    // test isValid
    //
    testOk1(ntContinuum->isValid());
    //
    // timeStamp ops
    //
    PVTimeStamp pvTimeStamp;
    if (ntContinuum->attachTimeStamp(pvTimeStamp))
    {
        testPass("timeStamp attach");

        // example how to set current time
        TimeStamp ts;
        ts.getCurrent();
        pvTimeStamp.set(ts);

        // example how to get EPICS time
        TimeStamp ts2;
        pvTimeStamp.get(ts2);
        testOk1(ts2.getEpicsSecondsPastEpoch() != 0);
    }
    else
        testFail("timeStamp attach fail");

    //
    // alarm ops
    //
    PVAlarm pvAlarm;
    if (ntContinuum->attachAlarm(pvAlarm))
    {
        testPass("alarm attach");

        // example how to set an alarm
        Alarm alarm;
        alarm.setStatus(deviceStatus);
        alarm.setSeverity(minorAlarm);
        alarm.setMessage("simulation alarm");
        pvAlarm.set(alarm);
    }
    else
        testFail("alarm attach fail");

    //
    // set descriptor
    //
    ntContinuum->getDescriptor()->put("This is a test NTContinuum");

    // dump NTContinuum
    std::cout << *ntContinuum->getPVStructure() << std::endl;

}
void test_nthistogram()
{
    testDiag("test_nthistogram");

    NTHistogramBuilderPtr builder = NTHistogram::createBuilder();
    testOk(builder.get() != 0, "Got builder");

    NTHistogramPtr ntHistogram = builder->
            value(pvInt)->
            addDescriptor()->
            addAlarm()->
            addTimeStamp()->
            add("extra1",fieldCreate->createScalar(pvString)) ->
            add("extra2",fieldCreate->createScalarArray(pvString)) ->
            create();
    testOk1(ntHistogram.get() != 0);

    testOk1(ntHistogram->getPVStructure().get() != 0);
    testOk1(ntHistogram->getDescriptor().get() != 0);
    testOk1(ntHistogram->getAlarm().get() != 0);
    testOk1(ntHistogram->getTimeStamp().get() != 0);
    testOk1(ntHistogram->getRanges().get() != 0);
    testOk1(ntHistogram->getValue().get() != 0);
    testOk1(ntHistogram->getValue<PVIntArray>().get() != 0);
    //
    // example how to set ranges
    //
    PVDoubleArray::svector newRanges;
    newRanges.push_back(-100.0);
    newRanges.push_back(0.0);
    newRanges.push_back(100.0);

    PVDoubleArrayPtr pvRangesField = ntHistogram->getRanges();
    pvRangesField->replace(freeze(newRanges));

    //
    // example how to get ranges
    //
    PVDoubleArray::const_svector ranges(pvRangesField->view());

    testOk1(ranges.size() == 3);
    testOk1(ranges[0] == -100.0);
    testOk1(ranges[1] == 0.0);
    testOk1(ranges[2] == 100.0);

    //
    // example how to set value
    //
    PVIntArray::svector newValue;
    newValue.push_back(1);
    newValue.push_back(2);

    PVIntArrayPtr pvValueField = ntHistogram->getValue<PVIntArray>();
    pvValueField->replace(freeze(newValue));

    //
    // example how to get values for each bin
    //
    PVIntArray::const_svector value(pvValueField->view());

    testOk1(value.size() == 2);
    testOk1(value[0] == 1);
    testOk1(value[1] == 2);

    //
    // test isValid
    //
    testOk1(ntHistogram->isValid());
    //
    // timeStamp ops
    //
    PVTimeStamp pvTimeStamp;
    if (ntHistogram->attachTimeStamp(pvTimeStamp))
    {
        testPass("timeStamp attach");

        // example how to set current time
        TimeStamp ts;
        ts.getCurrent();
        pvTimeStamp.set(ts);

        // example how to get EPICS time
        TimeStamp ts2;
        pvTimeStamp.get(ts2);
        testOk1(ts2.getEpicsSecondsPastEpoch() != 0);
    }
    else
        testFail("timeStamp attach fail");

    //
    // alarm ops
    //
    PVAlarm pvAlarm;
    if (ntHistogram->attachAlarm(pvAlarm))
    {
        testPass("alarm attach");

        // example how to set an alarm
        Alarm alarm;
        alarm.setStatus(deviceStatus);
        alarm.setSeverity(minorAlarm);
        alarm.setMessage("simulation alarm");
        pvAlarm.set(alarm);
    }
    else
        testFail("alarm attach fail");

    //
    // set descriptor
    //
    ntHistogram->getDescriptor()->put("This is a test NTHistogram");

    // dump NTHistogram
    std::cout << *ntHistogram->getPVStructure() << std::endl;

}
static void test()
{
    NTScalarMultiChannelBuilderPtr builder = NTScalarMultiChannel::createBuilder();
    testOk(builder.get() != 0, "Got builder");

    NTScalarMultiChannelPtr multiChannel = builder->
            addDescriptor()->
            addAlarm()->
            addTimeStamp()->
            addSeverity() ->
            add("extra1",fieldCreate->createScalar(pvString)) ->
            add("extra2",fieldCreate->createScalarArray(pvString)) ->
            create();
    testOk1(multiChannel.get() != 0);

    PVStructurePtr pvStructure = multiChannel->getPVStructure();
    testOk1(pvStructure.get()!=NULL);
    testOk1(NTScalarMultiChannel::is_a(pvStructure->getStructure()));
    size_t nchan = 3;
    shared_vector<string> names(nchan);
    names[0] = "channel 0";
    names[1] = "channel 1";
    names[2] = "channel 2";
    shared_vector<const string> channelNames(freeze(names));
    PVStringArrayPtr pvChannelName = multiChannel->getChannelName();
    pvChannelName->replace(channelNames);
    if(debug) {cout << *pvStructure << endl;}
    multiChannel = builder->
            value(pvDouble) ->
            addDescriptor()->
            addAlarm()->
            addTimeStamp()->
            addSeverity() ->
            addIsConnected() ->
            create();
    testOk1(multiChannel.get() != 0);
    pvStructure = multiChannel->getPVStructure();
    if(debug) {cout << *pvStructure << endl;}
    pvChannelName = multiChannel->getChannelName();
    pvChannelName->replace(channelNames);
    PVDoubleArrayPtr pvValue = multiChannel->getValue<PVDoubleArray>();
    PVDoubleArray::svector doubles(nchan);
    doubles.resize(nchan);
    doubles[0] = 3.14159;
    doubles[1] = 2.71828;
    doubles[2] = 137.036;
    pvValue->replace(freeze(doubles));
    shared_vector<int32> severities(nchan);
    severities[0] = 0;
    severities[1] = 1;
    severities[2] = 2;
    PVIntArrayPtr pvSeverity = multiChannel->getSeverity();
    pvSeverity->replace(freeze(severities));
    if(debug) {cout << *pvStructure << endl;}
    PVBooleanArrayPtr pvIsConnected = multiChannel->getIsConnected();
    shared_vector<const epics::pvData::boolean> isConnected = pvIsConnected->view();
    multiChannel = builder->
            value(pvDouble) ->
            addDescriptor()->
            addAlarm()->
            addTimeStamp()->
            addSeverity() ->
            addStatus() ->
            addMessage() ->
            addSecondsPastEpoch() ->
            addNanoseconds() ->
            addUserTag() ->
            addIsConnected() ->
            create();
    testOk1(multiChannel.get() != 0);
    pvStructure = multiChannel->getPVStructure();
    if(debug) {cout << *pvStructure << endl;}
    testOk1(NTScalarMultiChannel::isCompatible(pvStructure)==true);
    PVStructurePtr pvTimeStamp = multiChannel->getTimeStamp();
    testOk1(pvTimeStamp.get() !=0);
    PVStructurePtr pvAlarm = multiChannel->getAlarm();
    testOk1(pvAlarm.get() !=0);
    pvValue = multiChannel->getValue<PVDoubleArray>();
    testOk1(pvValue.get() !=0);
    pvChannelName = multiChannel->getChannelName();
    testOk1(pvChannelName.get() !=0);
    pvIsConnected = multiChannel->getIsConnected();
    testOk1(pvIsConnected.get() !=0);
    pvSeverity = multiChannel->getSeverity();
    testOk1(pvSeverity.get() !=0);
    PVIntArrayPtr pvStatus = multiChannel->getStatus();
    testOk1(pvStatus.get() !=0);
    PVStringArrayPtr pvMessage = multiChannel->getMessage();
    testOk1(pvMessage.get() !=0);
    PVLongArrayPtr pvSecondsPastEpoch = multiChannel->getSecondsPastEpoch();
    testOk1(pvSecondsPastEpoch.get() !=0);
    PVIntArrayPtr pvNanoseconds = multiChannel->getNanoseconds();
    testOk1(pvNanoseconds.get() !=0);
    PVIntArrayPtr pvUserTag = multiChannel->getUserTag();
    testOk1(pvUserTag.get() !=0);
    PVStringPtr pvDescriptor = multiChannel->getDescriptor();
    testOk1(pvDescriptor.get() !=0);
}
static void testDoubleArray()
{
    testDiag("== testDoubleArray ==");
    StructureConstPtr structure =
       fieldCreate->createFieldBuilder() ->
            add("alarm",standardField->alarm()) ->
            add("timeStamp",standardField->timeStamp()) ->
            addArray("value",pvDouble) ->
            createStructure();

    PvaClientPutDataPtr pvaData = PvaClientPutData::create(structure);
    PVDoubleArrayPtr pvalue = pvaData->getPVStructure() ->
        getSubField<PVDoubleArray>("value");

    size_t len = 5;
    shared_vector<double> value(len);
    for (size_t i=0; i<len; ++i)
        value[i] = i * 10.0;
    pvalue->replace(freeze(value));

    BitSetPtr change = pvaData->getChangedBitSet();
    size_t valueOffset = pvalue->getFieldOffset();
    testOk(change->cardinality()==1,"1 field changed");
    testOk(change->get(valueOffset),"value changed");

    testOk(pvaData->hasValue(),"hasValue");
    testOk(!pvaData->isValueScalar(),"!isValueScalar");
    testOk(pvaData->isValueScalarArray(),"isValueScalarArray");

    try {
        testOk(!!pvaData->getValue(), "getValue");
    } catch (std::runtime_error e) {
        testFail("getValue exception '%s'", e.what());
    }

    try {
        testOk(!pvaData->getScalarValue(), "!getScalarValue");
    } catch (std::runtime_error e) {
        testPass("getScalarValue exception '%s'", e.what());
    }
    try {
        testOk(!!pvaData->getArrayValue(), "getArrayValue");
    } catch (std::runtime_error e) {
        testFail("getArrayValue exception '%s'", e.what());
    }
    try {
        testOk(!!pvaData->getScalarArrayValue(), "getScalarArrayValue");
    } catch (std::runtime_error e) {
        testPass("getScalarArrayValue exception '%s'", e.what());
    }

    try {
        testFail("getDouble %g", pvaData->getDouble());
    } catch (std::runtime_error e) {
        testPass("getDouble exception '%s'", e.what());
    }
    try {
        testFail("getString %s", pvaData->getString().c_str());
    } catch (std::runtime_error e) {
        testPass("getString exception '%s'", e.what());
    }

    try {
        shared_vector<const double> value = pvaData->getDoubleArray();
        testPass("getDoubleArray");
    } catch (std::runtime_error e) {
        testFail("getDoubleArray exception '%s'", e.what());
    }
    try {
        shared_vector<const string> value = pvaData->getStringArray();
    } catch (std::runtime_error e) {
        testPass("getStringArray exception '%s'", e.what());
    }

    try {
        pvaData->putDouble(5.0);
        testFail("putDouble");
    } catch (std::runtime_error e) {
        testPass("putDouble exception '%s'", e.what());
    }
    try {
        pvaData->putString("1e5");
        testFail("putString");
    } catch (std::runtime_error e) {
        testPass("putString exception '%s'", e.what());
    }

    try {
        size_t len = 2;
        shared_vector<double> val(len);
        for (size_t i=0; i<len; ++i)
            val[i] = (i+1) * 2.0;
        pvaData->putDoubleArray(freeze(val));
        testPass("putDoubleArray");
    } catch (std::runtime_error e) {
        testFail("putDoubleArray exception '%s'", e.what());
    }
    try {
        size_t len = 2;
        shared_vector<string> val(len);
        val[0] = "one"; val[1] = "two";
        pvaData->putStringArray(freeze(val));
        testFail("putStringArray");
    } catch (std::runtime_error e) {
        testPass("putStringArray exception '%s'", e.what());
    }
}