static void createVariantUnionRecord(
    PVDatabasePtr const &master,
    string const &recordName)
{
    StructureConstPtr top = fieldCreate->createFieldBuilder()->
         add("value",fieldCreate->createVariantUnion())->
         createStructure();
    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(top);
    PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure);
    bool result = master->addRecord(pvRecord);
    if(!result) cout<< "record " << recordName << " not added" << endl;
}
namespace epics { namespace pvaClient { 

static FieldCreatePtr fieldCreate = getFieldCreate(); 
static const string pvaClientName = "pvaClient";
static const string defaultProvider = "pva";
static UnionConstPtr variantUnion = fieldCreate->createVariantUnion();

namespace pvaClientPvt {

    static size_t numberPvaClient = 0;
    static bool firstTime = true;
    static Mutex mutex;
    
    class StartStopClientFactory {
    public:
        static void PvaClientBeingConstructed()
        {
            bool saveFirst = false;
            { 
                 Lock xx(mutex);
                 ++numberPvaClient;
                 saveFirst = firstTime;
                 firstTime = false;
            }
            if(saveFirst) {
                ClientFactory::start();
                CAClientFactory::start();
            }
        }
    
        static void PvaClientBeingDestroyed() {
            size_t numLeft = 0;
            {
                 Lock xx(mutex);
                 --numberPvaClient;
                  numLeft = numberPvaClient;
            }
            if(numLeft<=0) {
                ClientFactory::stop();
                CAClientFactory::stop();
            }
        }
    };

} // namespace pvaClientPvt

class PvaClientChannelCache
{
public:
    PvaClientChannelCache(){}
    ~PvaClientChannelCache(){
         destroy();
     }
    void destroy() {
       pvaClientChannelMap.clear();
    }
    PvaClientChannelPtr getChannel(
        string const & channelName,
        string const & providerName);
    void addChannel(PvaClientChannelPtr const & pvaClientChannel);
    void removeChannel(string const & channelName,string const & providerName);
    void showCache();
    size_t cacheSize();
private:
    map<string,PvaClientChannelPtr> pvaClientChannelMap;
};
   
PvaClientChannelPtr PvaClientChannelCache::getChannel(
    string const & channelName,
    string const & providerName)
{
    string name = channelName + providerName;
    map<string,PvaClientChannelPtr>::iterator iter = pvaClientChannelMap.find(name);
    if(iter!=pvaClientChannelMap.end()) return iter->second;
    return PvaClientChannelPtr();
}

void PvaClientChannelCache::addChannel(PvaClientChannelPtr const & pvaClientChannel)
{
     Channel::shared_pointer channel = pvaClientChannel->getChannel();
     string name = channel->getChannelName()
         + channel->getProvider()->getProviderName();
     pvaClientChannelMap.insert(std::pair<string,PvaClientChannelPtr>(
         name,pvaClientChannel));
}

void PvaClientChannelCache::removeChannel(
        string const & channelName,
        string const & providerName)
{
    string name = channelName + providerName;
    map<string,PvaClientChannelPtr>::iterator iter = pvaClientChannelMap.find(name);
    if(iter!=pvaClientChannelMap.end()) pvaClientChannelMap.erase(iter);
}

void PvaClientChannelCache::showCache()
{
    map<string,PvaClientChannelPtr>::iterator iter;
    for(iter = pvaClientChannelMap.begin(); iter != pvaClientChannelMap.end(); ++iter)
    {
         PvaClientChannelPtr pvaChannel = iter->second;
         Channel::shared_pointer channel = pvaChannel->getChannel();
         string channelName = channel->getChannelName();
         string providerName = channel->getProvider()->getProviderName();
         cout << "channel " << channelName << " provider " << providerName << endl;
         cout << "  get and put cacheSize " << pvaChannel->cacheSize() << endl;
         pvaChannel->showCache();
    }
    
}

size_t PvaClientChannelCache::cacheSize()
{
    return pvaClientChannelMap.size();

}

using namespace epics::pvaClient::pvaClientPvt;

PvaClientPtr PvaClient::create()
{
    PvaClientPtr xx(new PvaClient());
    StartStopClientFactory::PvaClientBeingConstructed();
    return xx;
}


PvaClient::PvaClient()
:   pvaClientChannelCache(new PvaClientChannelCache()),
    isDestroyed(false)
{
}

PvaClient::~PvaClient() {
    destroy();
}

void PvaClient::destroy()
{
    {
        Lock xx(mutex);
        if(isDestroyed) return;
        isDestroyed = true;
    }
    pvaClientChannelCache.reset();
    StartStopClientFactory::PvaClientBeingDestroyed();
}

string PvaClient:: getRequesterName()
{
    static string name("pvaClient");
    RequesterPtr req = requester.lock();
    if(req) {
         return req->getRequesterName();
    }
    return name;
}

void  PvaClient::message(
        string const & message,
        MessageType messageType)
{
    RequesterPtr req = requester.lock();
    if(req) {
         req->message(message,messageType);
         return;
    }
    cout << getMessageTypeName(messageType) << " " << message << endl;
}

PvaClientChannelPtr PvaClient::channel(
        std::string const & channelName,
        std::string const & providerName,
        double timeOut)
{
    PvaClientChannelPtr pvaClientChannel = 
        pvaClientChannelCache->getChannel(channelName,providerName);
    if(pvaClientChannel) return pvaClientChannel;
    pvaClientChannel = createChannel(channelName,providerName);
    pvaClientChannel->connect(timeOut);
    pvaClientChannelCache->addChannel(pvaClientChannel);
    return pvaClientChannel;
}

PvaClientChannelPtr PvaClient::createChannel(string const & channelName, string const & providerName)
{
     return PvaClientChannel::create(getPtrSelf(),channelName,providerName);
}

void PvaClient::setRequester(RequesterPtr const & requester)
{
    this->requester = requester;
}

void PvaClient::clearRequester()
{
    requester = Requester::weak_pointer();
}

void PvaClient::showCache()
{
    pvaClientChannelCache->showCache();
}


size_t PvaClient::cacheSize()
{
    return pvaClientChannelCache->cacheSize();
}

}}
StructureConstPtr NTNDArrayBuilder::createStructure()
{
    enum
    {
        DISCRIPTOR_INDEX,
        TIMESTAMP_INDEX,
        ALARM_INDEX,
        DISPLAY_INDEX
    };

    const size_t NUMBER_OF_INDICES = DISPLAY_INDEX+1;
    const size_t NUMBER_OF_STRUCTURES = 1 << NUMBER_OF_INDICES;

    Lock xx(mutex);

    static StructureConstPtr ntndarrayStruc[NUMBER_OF_STRUCTURES];
    static UnionConstPtr valueType;
    static StructureConstPtr codecStruc;
    static StructureConstPtr dimensionStruc;
    static StructureConstPtr attributeStruc;

    StructureConstPtr returnedStruc;

    size_t index = 0;
    if (descriptor) index  |= 1 << DISCRIPTOR_INDEX;
    if (timeStamp)  index  |= 1 << TIMESTAMP_INDEX;
    if (alarm)      index  |= 1 << ALARM_INDEX;
    if (display)    index  |= 1 << DISPLAY_INDEX;

    bool isExtended = !extraFieldNames.empty();

    if (isExtended || !ntndarrayStruc[index])
    {
        StandardFieldPtr standardField = getStandardField();
        FieldBuilderPtr fb = fieldCreate->createFieldBuilder();

        if (!valueType)
        {
            for (int i = pvBoolean; i < pvString; ++i)
            {
                ScalarType st = static_cast<ScalarType>(i);
                fb->addArray(std::string(ScalarTypeFunc::name(st)) + "Value", st);
            }
            valueType = fb->createUnion();                
        }

        if (!codecStruc)
        {
            codecStruc = fb->setId("codec_t")->
                add("name", pvString)->
                add("parameters", fieldCreate->createVariantUnion())->
                createStructure();
        }

        if (!dimensionStruc)
        {
            dimensionStruc = fb->setId("dimension_t")->
                add("size", pvInt)->
                add("offset",  pvInt)->
                add("fullSize",  pvInt)->
                add("binning",  pvInt)->
                add("reverse",  pvBoolean)->
                createStructure();
        }

        if (!attributeStruc)
        {
            attributeStruc = NTNDArrayAttribute::createBuilder()->createStructure();
        }

        fb->setId(NTNDArray::URI)->
            add("value", valueType)->
            add("codec", codecStruc)->
            add("compressedSize", pvLong)->
            add("uncompressedSize", pvLong)->
            addArray("dimension", dimensionStruc)->
            add("uniqueId", pvInt)->
            add("dataTimeStamp", standardField->timeStamp())->
            addArray("attribute", attributeStruc);

        if (descriptor)
            fb->add("descriptor", pvString);

        if (alarm)
            fb->add("alarm", standardField->alarm());

        if (timeStamp)
            fb->add("timeStamp", standardField->timeStamp());

        if (display)
            fb->add("display", standardField->display());

        size_t extraCount = extraFieldNames.size();
        for (size_t i = 0; i< extraCount; i++)
            fb->add(extraFieldNames[i], extraFields[i]);

        returnedStruc = fb->createStructure();

        if (!isExtended)
            ntndarrayStruc[index] = returnedStruc; 
    }
    else
    {
        return ntndarrayStruc[index];
    }

    return returnedStruc;
}