From 5137b90f56f15d341b2a5ac6e4d9fa5d1473bbdd Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 6 Sep 2017 13:40:02 -0500 Subject: [PATCH 01/15] minor client message handling avoid unnecessary dynamic_cast --- src/remoteClient/clientContextImpl.cpp | 26 +++++++++++++------------- src/server/baseChannelRequester.cpp | 2 +- src/server/responseHandlers.cpp | 1 + src/utils/requester.cpp | 2 +- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index a3c965e..80b2b08 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -2874,23 +2874,23 @@ public: AbstractClientResponseHandler::handleResponse(responseFrom, transport, version, command, payloadSize, payloadBuffer); transport->ensureData(5); + int32 ioid = payloadBuffer->getInt(); + MessageType type = (MessageType)payloadBuffer->getByte(); - // TODO optimize - ResponseRequest::shared_pointer rr = _context.lock()->getResponseRequest(payloadBuffer->getInt()); - if (rr.get()) + string message = SerializeHelper::deserializeString(payloadBuffer, transport.get()); + + bool shown = false; + ResponseRequest::shared_pointer rr = _context.lock()->getResponseRequest(ioid); + if (rr) { - DataResponse::shared_pointer nrr = dynamic_pointer_cast(rr); - if (nrr.get()) - { - Requester::shared_pointer requester = nrr->getRequester(); - if (requester.get()) { - MessageType type = (MessageType)payloadBuffer->getByte(); - string message = SerializeHelper::deserializeString(payloadBuffer, transport.get()); - requester->message(message, type); - } + Requester::shared_pointer requester = rr->getRequester(); + if (requester) { + requester->message(message, type); + shown = true; } } - + if(!shown) + std::cerr<<"Orphaned server message "<startMessage((int8)18, sizeof(int32)/sizeof(int8) + 1); + control->startMessage((int8)CMD_MESSAGE, sizeof(int32)/sizeof(int8) + 1); buffer->putInt(_ioid); buffer->putByte((int8)_messageType); epics::pvData::SerializeHelper::serializeString(_message, buffer, control); diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 8c30a33..4a53222 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -129,6 +129,7 @@ ServerResponseHandler::ServerResponseHandler(ServerContextImpl::shared_pointer c m_handlerTable[CMD_GET_FIELD].reset(new ServerGetFieldHandler(context)); /* 17 - get field response */ m_handlerTable[CMD_MESSAGE] = badResponse; /* 18 - message to Requester */ m_handlerTable[CMD_MULTIPLE_DATA] = badResponse; /* 19 - grouped monitors */ + m_handlerTable[CMD_RPC].reset(new ServerRPCHandler(context)); /* 20 - RPC response */ m_handlerTable[CMD_CANCEL_REQUEST].reset(new ServerCancelRequestHandler(context)); /* 21 - cancel request */ } diff --git a/src/utils/requester.cpp b/src/utils/requester.cpp index 6927e68..bd2920f 100644 --- a/src/utils/requester.cpp +++ b/src/utils/requester.cpp @@ -35,7 +35,7 @@ string getMessageTypeName(MessageType messageType) void Requester::message(std::string const & message,MessageType messageType) { - std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")\n"; + std::cerr << "[" << getRequesterName() << "] " << getMessageTypeName(messageType) << " : " << message << "\n"; } }} From 25c7da43f2c77ea2c3709bfddb82c317659b33ae Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 6 Sep 2017 13:49:46 -0500 Subject: [PATCH 02/15] pvac: better Requester names pass through the client name (aka the channel name) --- src/client/clientGet.cpp | 5 ++++- src/client/clientMonitor.cpp | 5 ++++- src/client/clientRPC.cpp | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/client/clientGet.cpp b/src/client/clientGet.cpp index aaa69fe..25f4d10 100644 --- a/src/client/clientGet.cpp +++ b/src/client/clientGet.cpp @@ -72,7 +72,10 @@ struct GetPutter : public pva::ChannelPutRequester, } virtual std::string getRequesterName() OVERRIDE FINAL - { return "GetPutter"; } + { + Guard G(mutex); + return op ? op->getChannel()->getRequesterName() : ""; + } virtual void channelPutConnect( const epics::pvData::Status& status, diff --git a/src/client/clientMonitor.cpp b/src/client/clientMonitor.cpp index 2d6e4fa..33fe905 100644 --- a/src/client/clientMonitor.cpp +++ b/src/client/clientMonitor.cpp @@ -89,7 +89,10 @@ struct Monitor::Impl : public pva::MonitorRequester } virtual std::string getRequesterName() OVERRIDE FINAL - { return "RPCer"; } + { + Guard G(mutex); + return chan ? chan->getRequesterName() : ""; + } virtual void monitorConnect(pvd::Status const & status, diff --git a/src/client/clientRPC.cpp b/src/client/clientRPC.cpp index 4dff91a..a10ac55 100644 --- a/src/client/clientRPC.cpp +++ b/src/client/clientRPC.cpp @@ -83,7 +83,10 @@ struct RPCer : public pva::ChannelRPCRequester, } virtual std::string getRequesterName() OVERRIDE FINAL - { return "RPCer"; } + { + Guard G(mutex); + return op ? op->getChannel()->getRequesterName() : ""; + } virtual void channelRPCConnect( const epics::pvData::Status& status, From 94737721e3181b96352d8d0a1e2f14d97edcb260 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 6 Sep 2017 20:15:21 -0500 Subject: [PATCH 03/15] pvput use pvac --- pvtoolsSrc/pvput.cpp | 519 ++++++++--------------------------------- pvtoolsSrc/pvutils.cpp | 74 +++--- pvtoolsSrc/pvutils.h | 20 +- 3 files changed, 142 insertions(+), 471 deletions(-) diff --git a/pvtoolsSrc/pvput.cpp b/pvtoolsSrc/pvput.cpp index e4cb427..9582845 100644 --- a/pvtoolsSrc/pvput.cpp +++ b/pvtoolsSrc/pvput.cpp @@ -1,5 +1,4 @@ #include -#include #include #include @@ -9,6 +8,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -30,237 +33,6 @@ using namespace epics::pvAccess; namespace { -size_t fromString(PVFieldPtr const & pv, StringArray const & from, size_t fromStartIndex); - -size_t fromString(PVScalarArrayPtr const &pv, StringArray const & from, size_t fromStartIndex = 0) -{ - int processed = 0; - size_t fromValueCount = from.size(); - - // first get count - if (fromStartIndex >= fromValueCount) - throw std::runtime_error("not enough values"); - - size_t count; - istringstream iss(from[fromStartIndex]); - iss >> count; - // not fail and entire value is parsed (e.g. to detect 1.2 parsing to 1) - if (iss.fail() || !iss.eof()) - throw runtime_error("failed to parse element count value (uint) of field '" + pv->getFieldName() + "' from string value '" + from[fromStartIndex] + "'"); - fromStartIndex++; - processed++; - - if ((fromStartIndex+count) > fromValueCount) - { - throw runtime_error("not enough array values for field " + pv->getFieldName()); - } - - PVStringArray::svector valueList(count); - std::copy(from.begin() + fromStartIndex, from.begin() + fromStartIndex + count, valueList.begin()); - processed += count; - - pv->putFrom(freeze(valueList)); - - return processed; -} - -size_t fromString(PVStructurePtr const & pvStructure, StringArray const & from, size_t fromStartIndex); - -size_t fromString(PVStructureArrayPtr const &pv, StringArray const & from, size_t fromStartIndex = 0) -{ - int processed = 0; - size_t fromValueCount = from.size(); - - // first get count - if (fromStartIndex >= fromValueCount) - throw std::runtime_error("not enough values"); - - size_t numberOfStructures; - istringstream iss(from[fromStartIndex]); - iss >> numberOfStructures; - // not fail and entire value is parsed (e.g. to detect 1.2 parsing to 1) - if (iss.fail() || !iss.eof()) - throw runtime_error("failed to parse element count value (uint) of field '" + pv->getFieldName() + "' from string value '" + from[fromStartIndex] + "'"); - fromStartIndex++; - processed++; - - PVStructureArray::svector pvStructures; - pvStructures.reserve(numberOfStructures); - - PVDataCreatePtr pvDataCreate = getPVDataCreate(); - for (size_t i = 0; i < numberOfStructures; ++i) - { - PVStructurePtr pvStructure = pvDataCreate->createPVStructure(pv->getStructureArray()->getStructure()); - size_t count = fromString(pvStructure, from, fromStartIndex); - processed += count; - fromStartIndex += count; - pvStructures.push_back(pvStructure); - } - - pv->replace(freeze(pvStructures)); - - return processed; -} - -size_t fromString(PVUnionArrayPtr const & pvUnionArray, StringArray const & from, size_t fromStartIndex); - -size_t fromString(PVUnionPtr const & pvUnion, StringArray const & from, size_t fromStartIndex = 0) -{ - if (pvUnion->getUnion()->isVariant()) - throw std::runtime_error("cannot handle variant unions"); - - size_t fromValueCount = from.size(); - - if (fromStartIndex >= fromValueCount) - throw std::runtime_error("not enough values"); - - string selector = from[fromStartIndex++]; - PVFieldPtr pv = pvUnion->select(selector); - if (!pv) - throw std::runtime_error("invalid union selector value '" + selector + "'"); - - size_t processed = fromString(pv, from, fromStartIndex); - return processed + 1; -} - -size_t fromString(PVUnionArrayPtr const &pv, StringArray const & from, size_t fromStartIndex = 0) -{ - int processed = 0; - size_t fromValueCount = from.size(); - - // first get count - if (fromStartIndex >= fromValueCount) - throw std::runtime_error("not enough values"); - - size_t numberOfUnions; - istringstream iss(from[fromStartIndex]); - iss >> numberOfUnions; - // not fail and entire value is parsed (e.g. to detect 1.2 parsing to 1) - if (iss.fail() || !iss.eof()) - throw runtime_error("failed to parse element count value (uint) of field '" + pv->getFieldName() + "' from string value '" + from[fromStartIndex] + "'"); - fromStartIndex++; - processed++; - - PVUnionArray::svector pvUnions; - pvUnions.reserve(numberOfUnions); - - PVDataCreatePtr pvDataCreate = getPVDataCreate(); - for (size_t i = 0; i < numberOfUnions; ++i) - { - PVUnionPtr pvUnion = pvDataCreate->createPVUnion(pv->getUnionArray()->getUnion()); - size_t count = fromString(pvUnion, from, fromStartIndex); - processed += count; - fromStartIndex += count; - pvUnions.push_back(pvUnion); - } - - pv->replace(freeze(pvUnions)); - - return processed; -} - -size_t fromString(PVStructurePtr const & pvStructure, StringArray const & from, size_t fromStartIndex = 0) -{ - // handle enum in a special way - if (pvStructure->getStructure()->getID() == "enum_t") - { - int32 index = -1; - PVInt::shared_pointer pvIndex = pvStructure->getSubField("index"); - if (!pvIndex) - throw std::runtime_error("enum_t structure does not have 'int index' field"); - - PVStringArray::shared_pointer pvChoices = pvStructure->getSubField("choices"); - if (!pvChoices) - throw std::runtime_error("enum_t structure does not have 'string choices[]' field"); - PVStringArray::const_svector choices(pvChoices->view()); - - if (enumMode == AutoEnum || enumMode == StringEnum) - { - shared_vector::const_iterator it = std::find(choices.begin(), choices.end(), from[fromStartIndex]); - if (it != choices.end()) - index = static_cast(it - choices.begin()); - else if (enumMode == StringEnum) - throw runtime_error("enum string value '" + from[fromStartIndex] + "' invalid"); - } - - if ((enumMode == AutoEnum && index == -1) || enumMode == NumberEnum) - { - istringstream iss(from[fromStartIndex]); - iss >> index; - // not fail and entire value is parsed (e.g. to detect 1.2 parsing to 1) - if (iss.fail() || !iss.eof()) - throw runtime_error("enum value '" + from[fromStartIndex] + "' invalid"); - - if (index < 0 || index >= static_cast(choices.size())) - throw runtime_error("index '" + from[fromStartIndex] + "' out of bounds"); - } - - pvIndex->put(index); - return 1; - } - - size_t processed = 0; - - PVFieldPtrArray const & fieldsData = pvStructure->getPVFields(); - if (fieldsData.size() != 0) { - size_t length = pvStructure->getStructure()->getNumberFields(); - for(size_t i = 0; i < length; i++) { - size_t count = fromString(fieldsData[i], from, fromStartIndex); - processed += count; - fromStartIndex += count; - } - } - - return processed; -} - -size_t fromString(PVFieldPtr const & fieldField, StringArray const & from, size_t fromStartIndex) -{ - try - { - switch (fieldField->getField()->getType()) - { - case scalar: - { - if (fromStartIndex >= from.size()) - throw std::runtime_error("not enough values"); - - PVScalarPtr pv = TR1::static_pointer_cast(fieldField); - getConvert()->fromString(pv, from[fromStartIndex]); - return 1; - } - - case scalarArray: - return fromString(TR1::static_pointer_cast(fieldField), from, fromStartIndex); - - case structure: - return fromString(TR1::static_pointer_cast(fieldField), from, fromStartIndex); - - case structureArray: - return fromString(TR1::static_pointer_cast(fieldField), from, fromStartIndex); - - case union_: - return fromString(TR1::static_pointer_cast(fieldField), from, fromStartIndex); - - case unionArray: - return fromString(TR1::static_pointer_cast(fieldField), from, fromStartIndex); - - default: - std::ostringstream oss; - oss << "fromString unsupported fieldType " << fieldField->getField()->getType(); - throw std::logic_error(oss.str()); - } - } - catch (std::exception &ex) - { - std::ostringstream os; - os << "failed to parse '" << fieldField->getField()->getID() << ' ' - << fieldField->getFieldName() << "'"; - os << ": " << ex.what(); - throw std::runtime_error(os.str()); - } -} - #define DEFAULT_TIMEOUT 3.0 #define DEFAULT_REQUEST "field(value)" @@ -299,11 +71,11 @@ void usage (void) } -void printValue(std::string const & channelName, PVStructure::shared_pointer const & pv) +void printValue(std::string const & channelName, PVStructure::const_shared_pointer const & pv) { if (mode == ValueOnlyMode) { - PVField::shared_pointer value = pv->getSubField("value"); + PVField::const_shared_pointer value = pv->getSubField("value"); if (value.get() == 0) { std::cerr << "no 'value' field" << std::endl; @@ -317,7 +89,7 @@ void printValue(std::string const & channelName, PVStructure::shared_pointer con // special case for enum if (valueType == structure) { - PVStructurePtr pvStructure = TR1::static_pointer_cast(value); + PVStructure::const_shared_pointer pvStructure = TR1::static_pointer_cast(value); if (pvStructure->getStructure()->getID() == "enum_t") { if (fieldSeparator == ' ') @@ -357,138 +129,65 @@ void printValue(std::string const & channelName, PVStructure::shared_pointer con std::cout << std::endl << *(pv.get()) << std::endl << std::endl; } -class ChannelPutRequesterImpl : public ChannelPutRequester +struct Putter : public pvac::ClientChannel::PutCallback { -private: - PVStructure::shared_pointer m_pvStructure; - BitSet::shared_pointer m_bitSet; - Mutex m_pointerMutex; - Mutex m_eventMutex; - auto_ptr m_event; - string m_channelName; - AtomicBoolean m_done; + epicsEvent wait; + epicsMutex lock; + bool done; + pvac::PutEvent::event_t result; + std::string message; -public: + Putter() :done(false) {} - ChannelPutRequesterImpl(std::string channelName) : m_channelName(channelName) + std::string jblob; + + typedef std::pair KV_t; + typedef std::vector pairs_t; + pairs_t pairs; + + virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) { - resetEvent(); - } + PVStructurePtr root(getPVDataCreate()->createPVStructure(build)); - virtual string getRequesterName() - { - return "ChannelPutRequesterImpl"; - } - - virtual void channelPutConnect(const epics::pvData::Status& status, - ChannelPut::shared_pointer const & channelPut, - epics::pvData::Structure::const_shared_pointer const & /*structure*/) - { - if (status.isSuccess()) - { - // show warning - if (!status.isOK()) + if(pairs.empty()) { + std::istringstream strm(jblob); + parseJSON(strm, root); + } else { + for(pairs_t::const_iterator it=pairs.begin(), end=pairs.end(); it!=end; ++it) { - std::cerr << "[" << m_channelName << "] channel put create: " << dump_stack_only_on_debug(status) << std::endl; + PVFieldPtr fld(root->getSubField(it->first)); + if(!fld) { + fprintf(stderr, "%s : Error: no such field\n", it->first.c_str()); + } else if(it->second[0]=='{' || it->second[0]=='[') { + std::istringstream strm(it->second); + parseJSON(strm, fld); + } else { + PVScalarPtr sfld(std::tr1::dynamic_pointer_cast(fld)); + if(!sfld) { + fprintf(stderr, "%s : Error: need a scalar field\n", it->first.c_str()); + } else { + sfld->putFrom(it->second); + } + } } + } - // get immediately old value - channelPut->get(); - } - else - { - std::cerr << "[" << m_channelName << "] failed to create channel put: " << dump_stack_only_on_debug(status) << std::endl; - m_event->signal(); - } + args.root = root; + //TODO: track fields actually set + args.tosend.set(0); } - virtual void getDone(const epics::pvData::Status& status, ChannelPut::shared_pointer const & /*channelPut*/, - epics::pvData::PVStructure::shared_pointer const & pvStructure, - epics::pvData::BitSet::shared_pointer const & bitSet) + virtual void putDone(const pvac::PutEvent& evt) { - if (status.isSuccess()) { - // show warning - if (!status.isOK()) - { - std::cerr << "[" << m_channelName << "] channel get: " << dump_stack_only_on_debug(status) << std::endl; - } - - m_done.set(); - - { - Lock lock(m_pointerMutex); - m_pvStructure = pvStructure; - // we always put all, so current bitSet is OK - m_bitSet = bitSet; - } - - } - else - { - std::cerr << "[" << m_channelName << "] failed to get: " << dump_stack_only_on_debug(status) << std::endl; + epicsGuard G(lock); + result = evt.event; + message = evt.message; + done = true; } - m_event->signal(); + wait.signal(); } - - virtual void putDone(const epics::pvData::Status& status, ChannelPut::shared_pointer const & /*channelPut*/) - { - if (status.isSuccess()) - { - // show warning - if (!status.isOK()) - { - std::cerr << "[" << m_channelName << "] channel put: " << dump_stack_only_on_debug(status) << std::endl; - } - - m_done.set(); - } - else - { - std::cerr << "[" << m_channelName << "] failed to put: " << dump_stack_only_on_debug(status) << std::endl; - } - - m_event->signal(); - } - - PVStructure::shared_pointer getStructure() - { - Lock lock(m_pointerMutex); - return m_pvStructure; - } - - BitSet::shared_pointer getBitSet() - { - Lock lock(m_pointerMutex); - return m_bitSet; - } - - void resetEvent() - { - Lock lock(m_eventMutex); - m_event.reset(new Event()); - m_done.clear(); - } - - bool waitUntilDone(double timeOut) - { - Event* event; - { - Lock lock(m_eventMutex); - event = m_event.get(); - } - - bool signaled = event->wait(timeOut); - if (!signaled) - { - std::cerr << "[" << m_channelName << "] timeout" << std::endl; - return false; - } - - return m_done.get(); - } - }; } // namespace @@ -618,14 +317,6 @@ int main (int argc, char *argv[]) address = uri.host; } - epics::pvAccess::ca::CAClientFactory::start(); - - ChannelProvider::shared_pointer provider(ChannelProviderRegistry::clients()->getProvider(providerName)); - if(!provider) { - std::cerr << "Unknown provider '"< 0) { @@ -657,6 +348,39 @@ int main (int argc, char *argv[]) values.push_back(argv[optind]); } + if(values.empty()) { + usage(); + fprintf(stderr, "\nNo values provided\n"); + return 1; + } + + Putter thework; + + if(values[0][0]=='{' && values.size()>1) { + usage(); + fprintf(stderr, "\nMissing quotes around JSON?\n"); + return 1; + } else if(values[0][0]=='{') { + // write entire blob + thework.jblob = values[0]; + } else { + for(size_t i=0, N=values.size(); icreateChannel(pvName, DefaultChannelRequester::build(), - ChannelProvider::PRIORITY_DEFAULT, address); - } catch(std::exception& e){ - std::cerr<<"Provider " << providerName<< " Failed to create channel \""< putRequesterImpl(new ChannelPutRequesterImpl(channel->getChannelName())); - if (mode != TerseMode && !quiet) - std::cout << "Old : "; - ChannelPut::shared_pointer channelPut = channel->createChannelPut(putRequesterImpl, pvRequest); - allOK &= putRequesterImpl->waitUntilDone(timeOut); - if (allOK) - { - if (mode != TerseMode && !quiet) - printValue(pvName, putRequesterImpl->getStructure()); - - // convert value from string - // since we access structure from another thread, we need to lock - { - ScopedLock lock(channelPut); - fromString(putRequesterImpl->getStructure(), values); - } - - // we do a put - putRequesterImpl->resetEvent(); - // note on bitSet: we get all, we set all - channelPut->put(putRequesterImpl->getStructure(), putRequesterImpl->getBitSet()); - allOK &= putRequesterImpl->waitUntilDone(timeOut); - - if (allOK) - { - // and than a get again to verify put - if (mode != TerseMode && !quiet) std::cout << "New : "; - putRequesterImpl->resetEvent(); - channelPut->get(); - allOK &= putRequesterImpl->waitUntilDone(timeOut); - if (allOK && !quiet) - printValue(pvName, putRequesterImpl->getStructure()); - } - } - } catch (std::out_of_range& oor) { - allOK = false; - std::cerr << "parse error: not enough values" << std::endl; - } catch (std::exception& ex) { - allOK = false; - std::cerr << ex.what() << std::endl; - } catch (...) { - allOK = false; - std::cerr << "unknown exception caught" << std::endl; + if (mode != TerseMode && !quiet) { + std::cout << "Old : "; + printValue(pvName, chan.get(timeOut, pvRequest)); } - return allOK ? 0 : 1; + pvac::Operation op(chan.put(&thework, pvRequest)); + + if(!thework.wait.wait(timeOut)) { + fprintf(stderr, "Put timeout\n"); + return 1; + } + + if (mode != TerseMode && !quiet) { + std::cout << "New : "; + } + printValue(pvName, chan.get(timeOut, pvRequest)); + + return 0; } diff --git a/pvtoolsSrc/pvutils.cpp b/pvtoolsSrc/pvutils.cpp index cdf8283..ab8e3c5 100644 --- a/pvtoolsSrc/pvutils.cpp +++ b/pvtoolsSrc/pvutils.cpp @@ -73,7 +73,7 @@ void printUserTag(bool flag) printUserTagFlag = flag; } -std::ostream& terse(std::ostream& o, PVField::shared_pointer const & pv) +std::ostream& terse(std::ostream& o, PVField::const_shared_pointer const & pv) { Type type = pv->getField()->getType(); switch (type) @@ -82,19 +82,19 @@ std::ostream& terse(std::ostream& o, PVField::shared_pointer const & pv) o << *(pv.get()); return o; case structure: - return terseStructure(o, TR1::static_pointer_cast(pv)); + return terseStructure(o, TR1::static_pointer_cast(pv)); break; case scalarArray: - return terseScalarArray(o, TR1::static_pointer_cast(pv)); + return terseScalarArray(o, TR1::static_pointer_cast(pv)); break; case structureArray: - return terseStructureArray(o, TR1::static_pointer_cast(pv)); + return terseStructureArray(o, TR1::static_pointer_cast(pv)); break; case union_: - return terseUnion(o, TR1::static_pointer_cast(pv)); + return terseUnion(o, TR1::static_pointer_cast(pv)); break; case unionArray: - return terseUnionArray(o, TR1::static_pointer_cast(pv)); + return terseUnionArray(o, TR1::static_pointer_cast(pv)); break; default: std::ostringstream msg("unknown Field type: "); @@ -127,45 +127,29 @@ std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure const & pvE return o; } -std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvEnumT) +std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvEnumT) { return printEnumT(o, *pvEnumT); } -std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvTimeT) +std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvTimeT) { -#define TIMETEXTLEN 32 - char timeText[TIMETEXTLEN]; + char timeText[32]; epicsTimeStamp epicsTS; - int32 userTag; - PVTimeStamp pvTimeStamp; - if (pvTimeStamp.attach(pvTimeT)) - { - TimeStamp ts; - pvTimeStamp.get(ts); + PVScalar::const_shared_pointer secf(pvTimeT->getSubField("secondsPastEpoch")), + nsecf(pvTimeT->getSubField("nanoseconds")), + tagf(pvTimeT->getSubField("userTag")); - userTag = ts.getUserTag(); + epicsTS.secPastEpoch = secf ? secf->getAs() : 0; + epicsTS.nsec = nsecf ? nsecf->getAs() : 0; - if (ts.getSecondsPastEpoch() == 0 && - ts.getNanoseconds() == 0) - { - o << ""; - if (printUserTagFlag) - o << separator << userTag; - return o; - } + epicsTS.secPastEpoch += POSIX_TIME_AT_EPICS_EPOCH; - epicsTS.secPastEpoch = ts.getEpicsSecondsPastEpoch(); - epicsTS.nsec = ts.getNanoseconds(); - } - else - throw std::runtime_error("invalid time_t structure"); - - epicsTimeToStrftime(timeText, TIMETEXTLEN, "%Y-%m-%dT%H:%M:%S.%03f", &epicsTS); + epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%dT%H:%M:%S.%03f", &epicsTS); o << timeText; - if (printUserTagFlag) - o << separator << userTag; + if (printUserTagFlag && tagf) + o << separator << tagf->getAs(); return o; } @@ -189,17 +173,17 @@ const char* statusNames[] = { "CLIENT" // 7 }; -std::ostream& printAlarmT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvAlarmT) +std::ostream& printAlarmT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvAlarmT) { - PVInt::shared_pointer pvSeverity = pvAlarmT->getSubField("severity"); + PVInt::const_shared_pointer pvSeverity = pvAlarmT->getSubField("severity"); if (!pvSeverity) throw std::runtime_error("alarm_t structure does not have 'int severity' field"); - PVInt::shared_pointer pvStatus = pvAlarmT->getSubField("status"); + PVInt::const_shared_pointer pvStatus = pvAlarmT->getSubField("status"); if (!pvStatus) throw std::runtime_error("alarm_t structure does not have 'int status' field"); - PVString::shared_pointer pvMessage = pvAlarmT->getSubField("message"); + PVString::const_shared_pointer pvMessage = pvAlarmT->getSubField("message"); if (!pvMessage) throw std::runtime_error("alarm_t structure does not have 'string message' field"); @@ -224,7 +208,7 @@ std::ostream& printAlarmT(std::ostream& o, epics::pvData::PVStructure::shared_po } -bool isTType(epics::pvData::PVStructure::shared_pointer const & pvStructure) +bool isTType(epics::pvData::PVStructure::const_shared_pointer const & pvStructure) { // "forget" about Ttype if disabled if (!formatTTypesFlag) @@ -236,7 +220,7 @@ bool isTType(epics::pvData::PVStructure::shared_pointer const & pvStructure) id == "alarm_t"); } -bool formatTType(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvStructure) +bool formatTType(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvStructure) { // special t-types support (enum_t and time_t, etc.) if (formatTTypesFlag) @@ -263,7 +247,7 @@ bool formatTType(std::ostream& o, epics::pvData::PVStructure::shared_pointer con } -std::ostream& terseStructure(std::ostream& o, PVStructure::shared_pointer const & pvStructure) +std::ostream& terseStructure(std::ostream& o, PVStructure::const_shared_pointer const & pvStructure) { if (!pvStructure) { @@ -289,7 +273,7 @@ std::ostream& terseStructure(std::ostream& o, PVStructure::shared_pointer const return o; } -std::ostream& terseUnion(std::ostream& o, PVUnion::shared_pointer const & pvUnion) +std::ostream& terseUnion(std::ostream& o, PVUnion::const_shared_pointer const & pvUnion) { if (!pvUnion || !pvUnion->get()) { @@ -300,7 +284,7 @@ std::ostream& terseUnion(std::ostream& o, PVUnion::shared_pointer const & pvUnio return terse(o, pvUnion->get()); } -std::ostream& terseScalarArray(std::ostream& o, PVScalarArray::shared_pointer const & pvArray) +std::ostream& terseScalarArray(std::ostream& o, const PVScalarArray::const_shared_pointer &pvArray) { size_t length = pvArray->getLength(); if (arrayCountFlag) @@ -331,7 +315,7 @@ std::ostream& terseScalarArray(std::ostream& o, PVScalarArray::shared_pointer co */ } -std::ostream& terseStructureArray(std::ostream& o, PVStructureArray::shared_pointer const & pvArray) +std::ostream& terseStructureArray(std::ostream& o, PVStructureArray::const_shared_pointer const & pvArray) { size_t length = pvArray->getLength(); if (arrayCountFlag) @@ -357,7 +341,7 @@ std::ostream& terseStructureArray(std::ostream& o, PVStructureArray::shared_poin return o; } -std::ostream& terseUnionArray(std::ostream& o, PVUnionArray::shared_pointer const & pvArray) +std::ostream& terseUnionArray(std::ostream& o, PVUnionArray::const_shared_pointer const & pvArray) { size_t length = pvArray->getLength(); if (arrayCountFlag) diff --git a/pvtoolsSrc/pvutils.h b/pvtoolsSrc/pvutils.h index 944164e..da4139c 100644 --- a/pvtoolsSrc/pvutils.h +++ b/pvtoolsSrc/pvutils.h @@ -9,25 +9,25 @@ void convertStructureArray(std::string*, epics::pvData::PVStructureArray * pvdat void terseSeparator(char c); void terseArrayCount(bool flag); -std::ostream& terse(std::ostream& o, epics::pvData::PVField::shared_pointer const & pv); -std::ostream& terseUnion(std::ostream& o, epics::pvData::PVUnion::shared_pointer const & pvUnion); -std::ostream& terseStructure(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvStructure); -std::ostream& terseScalarArray(std::ostream& o, epics::pvData::PVScalarArray::shared_pointer const & pvArray); -std::ostream& terseStructureArray(std::ostream& o, epics::pvData::PVStructureArray::shared_pointer const & pvArray); -std::ostream& terseUnionArray(std::ostream& o, epics::pvData::PVUnionArray::shared_pointer const & pvArray); +std::ostream& terse(std::ostream& o, epics::pvData::PVField::const_shared_pointer const & pv); +std::ostream& terseUnion(std::ostream& o, epics::pvData::PVUnion::const_shared_pointer const & pvUnion); +std::ostream& terseStructure(std::ostream& o, const epics::pvData::PVStructure::const_shared_pointer &pvStructure); +std::ostream& terseScalarArray(std::ostream& o, epics::pvData::PVScalarArray::const_shared_pointer const & pvArray); +std::ostream& terseStructureArray(std::ostream& o, epics::pvData::PVStructureArray::const_shared_pointer const & pvArray); +std::ostream& terseUnionArray(std::ostream& o, epics::pvData::PVUnionArray::const_shared_pointer const & pvArray); enum EnumMode { AutoEnum, NumberEnum, StringEnum }; void setEnumPrintMode(EnumMode mode); void formatTTypes(bool flag); -bool isTType(epics::pvData::PVStructure::shared_pointer const & pvStructure); -bool formatTType(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvStructure); +bool isTType(epics::pvData::PVStructure::const_shared_pointer const & pvStructure); +bool formatTType(std::ostream& o, const epics::pvData::PVStructure::const_shared_pointer &pvStructure); void printUserTag(bool flag); std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure const & pvEnumT); -std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvEnumT); -std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvTimeT); +std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvEnumT); +std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvTimeT); bool starts_with(const std::string& str, const std::string& part); From 71c3ec220b3251885b6f63fe86673265f83e12c3 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 7 Sep 2017 16:06:38 -0500 Subject: [PATCH 04/15] pvput bitset tracking --- pvtoolsSrc/pvput.cpp | 50 +++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/pvtoolsSrc/pvput.cpp b/pvtoolsSrc/pvput.cpp index 9582845..50da0a8 100644 --- a/pvtoolsSrc/pvput.cpp +++ b/pvtoolsSrc/pvput.cpp @@ -48,9 +48,14 @@ PrintMode mode = ValueOnlyMode; char fieldSeparator = ' '; -void usage (void) +bool debug = false; + +void usage (bool details=false) { - fprintf (stderr, "\nUsage: pvput [options] ...\n\n" + fprintf (stderr, + "Usage: pvput [options] \n\n" + " pvput [options] = ...\n" + "\n" " -h: Help: Print this message\n" " -v: Print version and exit\n" "\noptions:\n" @@ -66,8 +71,15 @@ void usage (void) " default: Auto - try value as enum string, then as index number\n" " -n: Force enum interpretation of values as numbers\n" " -s: Force enum interpretation of values as strings\n" - "\nexample: pvput double01 1.234\n\n" , DEFAULT_REQUEST, DEFAULT_TIMEOUT, DEFAULT_PROVIDER); + if(details) + fprintf (stderr, + "\nExamples:\n" + "\n" + " pvput double01 1.234\n\n" + "equivalent to:\n" + " pvput double01 value=1.234\n\n" + ); } @@ -151,7 +163,7 @@ struct Putter : public pvac::ClientChannel::PutCallback if(pairs.empty()) { std::istringstream strm(jblob); - parseJSON(strm, root); + parseJSON(strm, root, &args.tosend); } else { for(pairs_t::const_iterator it=pairs.begin(), end=pairs.end(); it!=end; ++it) { @@ -160,21 +172,22 @@ struct Putter : public pvac::ClientChannel::PutCallback fprintf(stderr, "%s : Error: no such field\n", it->first.c_str()); } else if(it->second[0]=='{' || it->second[0]=='[') { std::istringstream strm(it->second); - parseJSON(strm, fld); + parseJSON(strm, fld, &args.tosend); } else { PVScalarPtr sfld(std::tr1::dynamic_pointer_cast(fld)); if(!sfld) { fprintf(stderr, "%s : Error: need a scalar field\n", it->first.c_str()); } else { sfld->putFrom(it->second); + args.tosend.set(sfld->getFieldOffset()); } } } } args.root = root; - //TODO: track fields actually set - args.tosend.set(0); + if(debug) + std::cout<<"To be sent: "< G(thework.lock); + while(!thework.done) { + epicsGuardRelease U(G); + if(!thework.wait.wait(timeOut)) { + fprintf(stderr, "Put timeout\n"); + return 1; + } + } + } + + if(thework.result==pvac::PutEvent::Fail) { + fprintf(stderr, "Error: %s\n", thework.message.c_str()); } if (mode != TerseMode && !quiet) { @@ -418,5 +440,5 @@ int main (int argc, char *argv[]) } printValue(pvName, chan.get(timeOut, pvRequest)); - return 0; + return thework.result!=pvac::PutEvent::Success; } From 2d0ce8df36933036fdd6cf80cfd04403c6a5979f Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 7 Sep 2017 18:40:23 -0500 Subject: [PATCH 05/15] pvput 3.14 compat --- pvtoolsSrc/pvput.cpp | 162 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 141 insertions(+), 21 deletions(-) diff --git a/pvtoolsSrc/pvput.cpp b/pvtoolsSrc/pvput.cpp index 50da0a8..d86d004 100644 --- a/pvtoolsSrc/pvput.cpp +++ b/pvtoolsSrc/pvput.cpp @@ -1,31 +1,36 @@ #include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - #include #include #include #include #include -#include -#include +#include #include -#include "pvutils.cpp" +#include +#include +#include + +#include +#include +#include +#include + +#include + +#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1) +# include +# define USE_JSON +#endif + +#include +#include #include +#include "pvutils.cpp" + using namespace std; namespace TR1 = std::tr1; using namespace epics::pvData; @@ -72,17 +77,33 @@ void usage (bool details=false) " -n: Force enum interpretation of values as numbers\n" " -s: Force enum interpretation of values as strings\n" , DEFAULT_REQUEST, DEFAULT_TIMEOUT, DEFAULT_PROVIDER); - if(details) + if(details) { + fprintf (stderr, +#ifdef USE_JSON + " JSON support is present\n" +#else + " no JSON support (needs EPICS Base >=3.15.0.1)\n"; +#endif + ); fprintf (stderr, "\nExamples:\n" "\n" - " pvput double01 1.234\n\n" + " pvput double01 1.234\n" "equivalent to:\n" - " pvput double01 value=1.234\n\n" + " pvput double01 value=1.234\n" ); +#ifdef USE_JSON + fprintf (stderr, + "\n" + "Field values may be given with JSON syntax. eg. and array:\n" + "\n" + " pvput arr:pv \"[1.0, 2.0]\"\n" + "equivalent to:\n" + " pvput arr:pv value=\"[1.0, 2.0]\"\n"); +#endif + } } - void printValue(std::string const & channelName, PVStructure::const_shared_pointer const & pv) { if (mode == ValueOnlyMode) @@ -141,6 +162,78 @@ void printValue(std::string const & channelName, PVStructure::const_shared_point std::cout << std::endl << *(pv.get()) << std::endl << std::endl; } +void early(const char *inp, unsigned pos) +{ + fprintf(stderr, "Unexpected end of input: %s\n", inp); + throw std::runtime_error("Unexpected end of input"); +} + +// rudimentory parser for json array +// needed as long as Base < 3.15 is supported. +// for consistency, used with all version +void jarray(shared_vector& out, const char *inp) +{ + assert(inp[0]=='['); + const char * const orig = inp; + inp++; + + while(true) { + // starting a new token + + for(; *inp==' '; inp++) {} // skip leading whitespace + + if(*inp=='\0') early(inp, inp-orig); + + if(isalnum(*inp) || *inp=='+' || *inp=='-') { + // number + + const char *start = inp; + + while(isalnum(*inp) || *inp=='.' || *inp=='+' || *inp=='-') + inp++; + + if(*inp=='\0') early(inp, inp-orig); + + // inp points to first char after token + + out.push_back(std::string(start, inp-start)); + + } else if(*inp=='"') { + // quoted string + + const char *start = ++inp; // skip quote + + while(*inp!='\0' && *inp!='"') + inp++; + + if(*inp=='\0') early(inp, inp-orig); + + // inp points to trailing " + + out.push_back(std::string(start, inp-start)); + + inp++; // skip trailing " + + } else if(*inp==']') { + // no-op + } else { + fprintf(stderr, "Unknown token '%c' in \"%s\"", *inp, inp); + throw std::runtime_error("Unknown token"); + } + + for(; *inp==' '; inp++) {} // skip trailing whitespace + + if(*inp==',') inp++; + else if(*inp==']') break; + else { + fprintf(stderr, "Unknown token '%c' in \"%s\"", *inp, inp); + throw std::runtime_error("Unknown token"); + } + } + + std::cerr<<"jarray "< pairs_t; pairs_t pairs; + shared_vector jarr; + virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) { PVStructurePtr root(getPVDataCreate()->createPVStructure(build)); @@ -164,12 +259,27 @@ struct Putter : public pvac::ClientChannel::PutCallback if(pairs.empty()) { std::istringstream strm(jblob); parseJSON(strm, root, &args.tosend); + } else { for(pairs_t::const_iterator it=pairs.begin(), end=pairs.end(); it!=end; ++it) { PVFieldPtr fld(root->getSubField(it->first)); if(!fld) { - fprintf(stderr, "%s : Error: no such field\n", it->first.c_str()); + fprintf(stderr, "%s : Warning: no such field\n", it->first.c_str()); + // ignore + + } else if(it->second[0]=='[') { + shared_vector arr; + jarray(arr, it->second.c_str()); + + PVScalarArray* afld(dynamic_cast(fld.get())); + if(!afld) { + fprintf(stderr, "%s : Error not a scalar array field\n", it->first.c_str()); + throw std::runtime_error("Not a scalar array field"); + } + afld->putFrom(freeze(arr)); + args.tosend.set(afld->getFieldOffset()); + } else if(it->second[0]=='{' || it->second[0]=='[') { std::istringstream strm(it->second); parseJSON(strm, fld, &args.tosend); @@ -374,6 +484,10 @@ int main (int argc, char *argv[]) return 1; } else if(values[0][0]=='{') { // write entire blob +#ifndef USE_JSON + fprintf(stderr, "JSON syntax not supported by this build.\n"); + return 1; +#endif thework.jblob = values[0]; } else { for(size_t i=0, N=values.size(); i Date: Fri, 8 Sep 2017 11:48:13 -0500 Subject: [PATCH 06/15] more pvput: support "legacy" array mode (w/ size) --- pvtoolsSrc/pvput.cpp | 157 ++++++++++++++++++++++++++++++++----------- 1 file changed, 116 insertions(+), 41 deletions(-) diff --git a/pvtoolsSrc/pvput.cpp b/pvtoolsSrc/pvput.cpp index d86d004..3f96ac5 100644 --- a/pvtoolsSrc/pvput.cpp +++ b/pvtoolsSrc/pvput.cpp @@ -58,8 +58,15 @@ bool debug = false; void usage (bool details=false) { fprintf (stderr, - "Usage: pvput [options] \n\n" + "Usage: pvput [options] \n" + " pvput [options] [ ...]\n" " pvput [options] = ...\n" + " pvput [options] \n"); +#ifdef USE_JSON + fprintf (stderr, + " pvput [options] \n"); +#endif + fprintf (stderr, "\n" " -h: Help: Print this message\n" " -v: Print version and exit\n" @@ -80,26 +87,35 @@ void usage (bool details=false) if(details) { fprintf (stderr, #ifdef USE_JSON - " JSON support is present\n" + "\n JSON support is present\n" #else - " no JSON support (needs EPICS Base >=3.15.0.1)\n"; + "\n no JSON support (needs EPICS Base >=3.15.0.1)\n" #endif ); fprintf (stderr, "\nExamples:\n" "\n" - " pvput double01 1.234\n" - "equivalent to:\n" + " pvput double01 1.234 # shorthand\n" " pvput double01 value=1.234\n" + "\n" + " pvput arr:pv X 1.0 2.0 # shorthand (X is arbitrary and ignored)\n" + " pvput arr:pv \"[1.0, 2.0]\" # shorthand\n" + " pvput arr:pv value=\"[1.0, 2.0]\"\n" ); #ifdef USE_JSON fprintf (stderr, "\n" - "Field values may be given with JSON syntax. eg. and array:\n" + "Field values may be given with JSON syntax.\n" "\n" - " pvput arr:pv \"[1.0, 2.0]\"\n" - "equivalent to:\n" - " pvput arr:pv value=\"[1.0, 2.0]\"\n"); + "Complete structure\n" + "\n" + " pvput double01 '{\"value\":1.234}'\n" + "\n" + "Sub-structure(s)\n" + "\n" + " pvput group:pv some='{\"value\":1.234}' other='{\"value\":\"a string\"}'\n" + "\n" + ); #endif } } @@ -244,7 +260,8 @@ struct Putter : public pvac::ClientChannel::PutCallback Putter() :done(false) {} - std::string jblob; + typedef shared_vector bare_t; + bare_t bare; typedef std::pair KV_t; typedef std::vector pairs_t; @@ -254,13 +271,68 @@ struct Putter : public pvac::ClientChannel::PutCallback virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) { + if(debug) std::cerr<<"Server defined structure\n"<createPVStructure(build)); - if(pairs.empty()) { - std::istringstream strm(jblob); + if(bare.size()==1 && bare[0][0]=='{') { + if(debug) fprintf(stderr, "In JSON top mode\n"); +#ifdef USE_JSON + std::istringstream strm(bare[0]); parseJSON(strm, root, &args.tosend); +#else +#endif + + } else if(pairs.empty()) { + if(debug) fprintf(stderr, "In plain value mode\n"); + + PVFieldPtr fld(root->getSubField("value")); + Type ftype = fld->getField()->getType(); + + if(ftype==scalar) { + if(bare.size()!=1) { + throw std::runtime_error("Can't assign multiple values to scalar"); + } + PVScalar* sfld(static_cast(fld.get())); + sfld->putFrom(bare[0]); + args.tosend.set(sfld->getFieldOffset()); + + } else if(ftype==scalarArray) { + PVScalarArray* sfld(static_cast(fld.get())); + + // first element is "length" which we ignore for compatibility + bare.slice(1); + + sfld->putFrom(freeze(bare)); + args.tosend.set(sfld->getFieldOffset()); + + } else if(ftype==structure && fld->getField()->getID()=="enum_t") { + if(bare.size()!=1) { + throw std::runtime_error("Can't assign multiple values to enum"); + } + PVStructure* sfld(static_cast(fld.get())); + + PVScalar* idxfld(sfld->getSubFieldT("index").get()); + PVStringArray::const_svector choices(sfld->getSubFieldT("choices")->view()); + + bool found=false; + for(size_t i=0; iputFrom(i); + found=true; + } + } + + if(!found) { + // try to parse as integer + idxfld->putFrom(bare[0]); + } + + args.tosend.set(idxfld->getFieldOffset()); + } } else { + if(debug) fprintf(stderr, "In field=value mode\n"); + for(pairs_t::const_iterator it=pairs.begin(), end=pairs.end(); it!=end; ++it) { PVFieldPtr fld(root->getSubField(it->first)); @@ -282,7 +354,11 @@ struct Putter : public pvac::ClientChannel::PutCallback } else if(it->second[0]=='{' || it->second[0]=='[') { std::istringstream strm(it->second); +#ifdef USE_JSON parseJSON(strm, fld, &args.tosend); +#else + throw std::runtime_error("JSON support not built"); +#endif } else { PVScalarPtr sfld(std::tr1::dynamic_pointer_cast(fld)); if(!sfld) { @@ -478,39 +554,38 @@ int main (int argc, char *argv[]) Putter thework; - if(values[0][0]=='{' && values.size()>1) { - usage(); - fprintf(stderr, "\nMissing quotes around JSON?\n"); - return 1; - } else if(values[0][0]=='{') { - // write entire blob + for(size_t i=0, N=values.size(); i Date: Fri, 8 Sep 2017 15:29:28 -0500 Subject: [PATCH 07/15] pvput minor --- pvtoolsSrc/pvput.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/pvtoolsSrc/pvput.cpp b/pvtoolsSrc/pvput.cpp index 3f96ac5..5f485a5 100644 --- a/pvtoolsSrc/pvput.cpp +++ b/pvtoolsSrc/pvput.cpp @@ -247,7 +247,6 @@ void jarray(shared_vector& out, const char *inp) } } - std::cerr<<"jarray "< Date: Tue, 12 Sep 2017 12:02:26 -0500 Subject: [PATCH 08/15] pvput compat --- pvtoolsSrc/pvput.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvtoolsSrc/pvput.cpp b/pvtoolsSrc/pvput.cpp index 5f485a5..1da2473 100644 --- a/pvtoolsSrc/pvput.cpp +++ b/pvtoolsSrc/pvput.cpp @@ -316,7 +316,7 @@ struct Putter : public pvac::ClientChannel::PutCallback bool found=false; for(size_t i=0; iputFrom(i); + idxfld->putFrom(i); found=true; } } From de72e8de1ead6ce40874bad859a9311a737cb724 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 13 Sep 2017 09:19:27 -0500 Subject: [PATCH 09/15] travis-ci: fix c++11 builds --- .travis.yml | 4 ++-- ci/travis-build.sh | 2 +- ci/travis-prepare.sh | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7e62e96..14986ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,8 @@ addons: env: - BRBASE=3.16 - BRBASE=3.16 CMPLR=clang - - BRBASE=3.16 USR_CXXFLAGS=-std=c++11 - - BRBASE=3.16 USR_CXXFLAGS=-std=c++11 CMPLR=clang + - BRBASE=3.16 EXTRA="CMD_CXXFLAGS=-std=c++11" + - BRBASE=3.16 EXTRA="CMD_CXXFLAGS=-std=c++11" CMPLR=clang - BRBASE=3.16 WINE=32 TEST=NO STATIC=YES - BRBASE=3.16 WINE=32 TEST=NO STATIC=NO - BRBASE=3.15 diff --git a/ci/travis-build.sh b/ci/travis-build.sh index 36c49cf..a022471 100755 --- a/ci/travis-build.sh +++ b/ci/travis-build.sh @@ -1,7 +1,7 @@ #!/bin/sh set -e -x -make -j2 +make -j2 $EXTRA if [ "$TEST" != "NO" ] then diff --git a/ci/travis-prepare.sh b/ci/travis-prepare.sh index 2362a35..af68c18 100755 --- a/ci/travis-prepare.sh +++ b/ci/travis-prepare.sh @@ -65,5 +65,5 @@ cat << EOF > pvDataCPP/configure/RELEASE.local EPICS_BASE=$HOME/.source/epics-base EOF -make -j2 -C epics-base -make -j2 -C pvDataCPP +make -j2 -C epics-base $EXTRA +make -j2 -C pvDataCPP $EXTRA From ed9be79ff4dc69e9f23e252084493f52ad4a6ab7 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Thu, 21 Sep 2017 16:42:22 +0200 Subject: [PATCH 10/15] travis-ci: changes for EPICS 7 Base structure --- .ci/travis-build.sh | 21 ++++++ .ci/travis-prepare.sh | 172 ++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 21 +++--- ci/travis-build.sh | 10 --- ci/travis-prepare.sh | 69 ----------------- 5 files changed, 205 insertions(+), 88 deletions(-) create mode 100755 .ci/travis-build.sh create mode 100755 .ci/travis-prepare.sh delete mode 100755 ci/travis-build.sh delete mode 100755 ci/travis-prepare.sh diff --git a/.ci/travis-build.sh b/.ci/travis-build.sh new file mode 100755 index 0000000..6c76689 --- /dev/null +++ b/.ci/travis-build.sh @@ -0,0 +1,21 @@ +#!/bin/sh +set -e -x + +# set RTEMS to eg. "4.9" or "4.10" +# requires qemu, bison, flex, texinfo, install-info +if [ -n "$RTEMS" ] +then + # find local qemu-system-i386 + export PATH="$HOME/.cache/qemu/usr/bin:$PATH" + echo -n "Using QEMU: " + type qemu-system-i386 || echo "Missing qemu" + EXTRA=RTEMS_QEMU_FIXUPS=YES +fi + +make -j2 $EXTRA + +if [ "$TEST" != "NO" ] +then + make tapfiles + find . -name '*.tap' -print0 | xargs -0 -n1 prove -e cat -f +fi diff --git a/.ci/travis-prepare.sh b/.ci/travis-prepare.sh new file mode 100755 index 0000000..0307430 --- /dev/null +++ b/.ci/travis-prepare.sh @@ -0,0 +1,172 @@ +#!/bin/sh +set -e -x + +CURDIR="$PWD" + +QDIR="$HOME/.cache/qemu" + +if [ -n "$RTEMS" -a "$TEST" = "YES" ] +then + git clone --quiet --branch vme --depth 10 https://github.com/mdavidsaver/qemu.git "$HOME/.build/qemu" + cd "$HOME/.build/qemu" + + HEAD=`git log -n1 --pretty=format:%H` + echo "HEAD revision $HEAD" + + [ -e "$HOME/.cache/qemu/built" ] && BUILT=`cat "$HOME/.cache/qemu/built"` + echo "Cached revision $BUILT" + + if [ "$HEAD" != "$BUILT" ] + then + echo "Building QEMU" + git submodule --quiet update --init + + install -d "$HOME/.build/qemu/build" + cd "$HOME/.build/qemu/build" + + "$HOME/.build/qemu/configure" --prefix="$HOME/.cache/qemu/usr" --target-list=i386-softmmu --disable-werror + make -j2 + make install + + echo "$HEAD" > "$HOME/.cache/qemu/built" + fi +fi + +cat << EOF > $CURDIR/configure/RELEASE.local +EPICS_BASE=$HOME/.source/epics-base +EOF + +install -d "$HOME/.source" +cd "$HOME/.source" + +add_base_module() { + MODULE=$1 + BRANCH=$2 + ( cd epics-base/modules && \ + git clone --quiet --depth 5 --branch $MODULE/$BRANCH https://github.com/${REPOBASE:-epics-base}/epics-base.git $MODULE && \ + cd $MODULE && git log -n1 ) +} + +add_gh_module() { + MODULE=$1 + REPOOWNER=$2 + REPONAME=$3 + BRANCH=$4 + ( cd epics-base/modules && \ + git clone --quiet --depth 5 --branch $BRANCH https://github.com/$REPOOWNER/$REPONAME.git $MODULE && \ + cd $MODULE && git log -n1 ) +} + +add_gh_flat() { + MODULE=$1 + REPOOWNER=$2 + REPONAME=$3 + BRANCH=$4 + MODULE_UC=$(echo $MODULE | tr 'a-z' 'A-Z') + ( git clone --quiet --depth 5 --branch $BRANCH https://github.com/$REPOOWNER/$REPONAME.git $MODULE && \ + cd $MODULE && git log -n1 ) + cat < $CURDIR/configure/RELEASE.local > $MODULE/configure/RELEASE.local + cat << EOF >> $CURDIR/configure/RELEASE.local +${MODULE_UC}=$HOME/.source/$MODULE +EOF +} + +if [ "$BRBASE" ] +then + git clone --quiet --depth 5 --branch "$BRBASE" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base + (cd epics-base && git log -n1 ) + add_gh_flat pvData ${REPOPVD:-epics-base} pvDataCPP ${BRPVD:-master} +else + git clone --quiet --depth 5 --branch core/"${BRCORE:-master}" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base + ( cd epics-base && git log -n1 ) + add_base_module libcom "${BRLIBCOM:-master}" + add_gh_module pvData ${REPOPVD:-epics-base} pvDataCPP ${BRPVD:-master} +fi + +if [ -e configure/RELEASE.local ] +then + cat configure/RELEASE.local +fi + +EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch` + +# requires wine and g++-mingw-w64-i686 +if [ "$WINE" = "32" ] +then + echo "Cross mingw32" + sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw + cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw +CMPLR_PREFIX=i686-w64-mingw32- +EOF + cat << EOF >> epics-base/configure/CONFIG_SITE +CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw +EOF +fi + +if [ "$STATIC" = "YES" ] +then + echo "Build static libraries/executables" + cat << EOF >> epics-base/configure/CONFIG_SITE +SHARED_LIBRARIES=NO +STATIC_BUILD=YES +EOF +fi + +case "$CMPLR" in +clang) + echo "Host compiler is clang" + cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH +GNU = NO +CMPLR_CLASS = clang +CC = clang +CCC = clang++ +EOF + + # hack + sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon + + clang --version + ;; +*) + echo "Host compiler is default" + gcc --version + ;; +esac + +cat <> epics-base/configure/CONFIG_SITE +USR_CPPFLAGS += $USR_CPPFLAGS +USR_CFLAGS += $USR_CFLAGS +USR_CXXFLAGS += $USR_CXXFLAGS +EOF + +# set RTEMS to eg. "4.9" or "4.10" +# requires qemu, bison, flex, texinfo, install-info +if [ -n "$RTEMS" ] +then + echo "Cross RTEMS${RTEMS} for pc386" + install -d /home/travis/.cache + curl -L "https://github.com/mdavidsaver/rsb/releases/download/travis-20160306-2/rtems${RTEMS}-i386-trusty-20190306-2.tar.gz" \ + | tar -C /home/travis/.cache -xj + + sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS + cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS +RTEMS_VERSION=$RTEMS +RTEMS_BASE=/home/travis/.cache/rtems${RTEMS}-i386 +EOF + cat << EOF >> epics-base/configure/CONFIG_SITE +CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386 +EOF + + # find local qemu-system-i386 + export PATH="$HOME/.cache/qemu/usr/bin:$PATH" + echo -n "Using QEMU: " + type qemu-system-i386 || echo "Missing qemu" + EXTRA=RTEMS_QEMU_FIXUPS=YES +fi + +make -j2 -C epics-base $EXTRA + +if [ "$BRBASE" ] +then + make -j2 -C pvData $EXTRA +fi diff --git a/.travis.yml b/.travis.yml index 7e62e96..e26ce3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,16 +11,19 @@ addons: - perl - clang - g++-mingw-w64-i686 +install: + - ./.ci/travis-prepare.sh +script: + - ./.ci/travis-build.sh env: + - BRCORE=master BRLIBCOM=master BRPVD=master + - CMPLR=clang + - USR_CXXFLAGS=-std=c++11 + - CMPLR=clang USR_CXXFLAGS=-std=c++11 + - WINE=32 TEST=NO STATIC=YES + - WINE=32 TEST=NO STATIC=NO + - RTEMS=4.10 TEST=NO + - RTEMS=4.9 TEST=NO - BRBASE=3.16 - - BRBASE=3.16 CMPLR=clang - - BRBASE=3.16 USR_CXXFLAGS=-std=c++11 - - BRBASE=3.16 USR_CXXFLAGS=-std=c++11 CMPLR=clang - - BRBASE=3.16 WINE=32 TEST=NO STATIC=YES - - BRBASE=3.16 WINE=32 TEST=NO STATIC=NO - BRBASE=3.15 - BRBASE=3.14 -install: - - ./ci/travis-prepare.sh -script: - - ./ci/travis-build.sh diff --git a/ci/travis-build.sh b/ci/travis-build.sh deleted file mode 100755 index 36c49cf..0000000 --- a/ci/travis-build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -set -e -x - -make -j2 - -if [ "$TEST" != "NO" ] -then - make tapfiles - find . -name '*.tap' -print0 | xargs -0 -n1 prove -e cat -f -fi diff --git a/ci/travis-prepare.sh b/ci/travis-prepare.sh deleted file mode 100755 index 2362a35..0000000 --- a/ci/travis-prepare.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/sh -set -e -x - -cat << EOF > configure/RELEASE.local -EPICS_BASE=$HOME/.source/epics-base -PVDATA=$HOME/.source/pvDataCPP -EOF -cat configure/RELEASE.local - -install -d "$HOME/.source" -cd "$HOME/.source" - -git clone --quiet --depth 5 --branch "${BRBASE:-master}" https://github.com/${SRCBASE:-epics-base}/epics-base.git epics-base -git clone --quiet --depth 5 --branch "${BRPVD:-master}" https://github.com/${SRCPVD:-epics-base}/pvDataCPP.git pvDataCPP - -(cd epics-base && git log -n1 ) -(cd pvDataCPP && git log -n1 ) - -EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch` - -# requires wine and g++-mingw-w64-i686 -if [ "$WINE" = "32" ] -then - echo "Cross mingw32" - sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw - cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw -CMPLR_PREFIX=i686-w64-mingw32- -EOF - cat << EOF >> epics-base/configure/CONFIG_SITE -CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw -EOF -fi - -if [ "$STATIC" = "YES" ] -then - echo "Build static libraries/executables" - cat << EOF >> epics-base/configure/CONFIG_SITE -SHARED_LIBRARIES=NO -STATIC_BUILD=YES -EOF -fi - -case "$CMPLR" in -clang) - echo "Host compiler is clang" - cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH -GNU = NO -CMPLR_CLASS = clang -CC = clang -CCC = clang++ -EOF - - # hack - sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon - - clang --version - ;; -*) - echo "Host compiler is default" - gcc --version - ;; -esac - -cat << EOF > pvDataCPP/configure/RELEASE.local -EPICS_BASE=$HOME/.source/epics-base -EOF - -make -j2 -C epics-base -make -j2 -C pvDataCPP From 8552fa6a1d71915db284eca1249f7501ba8c01e2 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 26 Sep 2017 15:41:12 +0200 Subject: [PATCH 11/15] travis-ci: add ca and database as dependencies --- .ci/travis-prepare.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.ci/travis-prepare.sh b/.ci/travis-prepare.sh index 0307430..2251949 100755 --- a/.ci/travis-prepare.sh +++ b/.ci/travis-prepare.sh @@ -80,12 +80,14 @@ else git clone --quiet --depth 5 --branch core/"${BRCORE:-master}" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base ( cd epics-base && git log -n1 ) add_base_module libcom "${BRLIBCOM:-master}" + add_base_module ca "${BRCA:-master}" + add_base_module database "${BRDATABASE:-master}" add_gh_module pvData ${REPOPVD:-epics-base} pvDataCPP ${BRPVD:-master} fi -if [ -e configure/RELEASE.local ] +if [ -e $CURDIR/configure/RELEASE.local ] then - cat configure/RELEASE.local + cat $CURDIR/configure/RELEASE.local fi EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch` From f68fa741f00de80c22b2baed8f8e766148826b22 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 28 Sep 2017 16:47:43 -0500 Subject: [PATCH 12/15] pvtools oops --- pvtoolsSrc/pvutils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvtoolsSrc/pvutils.cpp b/pvtoolsSrc/pvutils.cpp index ab8e3c5..b0eab26 100644 --- a/pvtoolsSrc/pvutils.cpp +++ b/pvtoolsSrc/pvutils.cpp @@ -144,7 +144,7 @@ std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::const_shar epicsTS.secPastEpoch = secf ? secf->getAs() : 0; epicsTS.nsec = nsecf ? nsecf->getAs() : 0; - epicsTS.secPastEpoch += POSIX_TIME_AT_EPICS_EPOCH; + epicsTS.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH; epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%dT%H:%M:%S.%03f", &epicsTS); o << timeText; From 0a9797f9629af51d58aa6189224c4e79c169c55b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 28 Sep 2017 16:48:16 -0500 Subject: [PATCH 13/15] detect type change bug --- src/remoteClient/clientContextImpl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 80b2b08..c8b4d8d 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -2001,8 +2001,10 @@ public: // deserialize changedBitSet and data, and overrun bit set changedBitSet->deserialize(payloadBuffer, transport.get()); - if (m_up2datePVStructure && m_up2datePVStructure.get() != pvStructure.get()) + if (m_up2datePVStructure && m_up2datePVStructure.get() != pvStructure.get()) { + assert(pvStructure->getStructure().get()==m_up2datePVStructure->getStructure().get()); // TODO: missing some type change (pvStructure w/ fewer fields) pvStructure->copyUnchecked(*m_up2datePVStructure, *changedBitSet, true); + } pvStructure->deserialize(payloadBuffer, transport.get(), changedBitSet.get()); overrunBitSet->deserialize(payloadBuffer, transport.get()); From b1444b619260a3a56a203c0a472f180d87da0e4b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 29 Sep 2017 15:23:14 -0500 Subject: [PATCH 14/15] client: fix monitor crash on type change --- src/remoteClient/clientContextImpl.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index c8b4d8d..9bf41ed 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -1935,19 +1935,21 @@ public: m_releasedCount = 0; m_reportQueueStateInProgress = false; - // reuse on reconnect - if (m_lastStructure.get() == 0 || - *(m_lastStructure.get()) != *(structure.get())) { while (!m_monitorQueue.empty()) m_monitorQueue.pop(); + m_freeQueue.clear(); + + m_up2datePVStructure.reset(); + for (int32 i = 0; i < m_queueSize; i++) { PVStructure::shared_pointer pvStructure = getPVDataCreate()->createPVStructure(structure); MonitorElement::shared_pointer monitorElement(new MonitorElement(pvStructure)); m_freeQueue.push_back(monitorElement); } + m_lastStructure = structure; } } @@ -2002,7 +2004,7 @@ public: // deserialize changedBitSet and data, and overrun bit set changedBitSet->deserialize(payloadBuffer, transport.get()); if (m_up2datePVStructure && m_up2datePVStructure.get() != pvStructure.get()) { - assert(pvStructure->getStructure().get()==m_up2datePVStructure->getStructure().get()); // TODO: missing some type change (pvStructure w/ fewer fields) + assert(pvStructure->getStructure().get()==m_up2datePVStructure->getStructure().get()); pvStructure->copyUnchecked(*m_up2datePVStructure, *changedBitSet, true); } pvStructure->deserialize(payloadBuffer, transport.get(), changedBitSet.get()); From 9460fab294ab632aea6e530f53aa07981da68224 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 29 Sep 2017 16:22:19 -0500 Subject: [PATCH 15/15] pvac: ClientProvider ctor from ChannelProvider --- src/client/client.cpp | 9 +++++++++ src/client/pva/client.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/client/client.cpp b/src/client/client.cpp index 8a68530..b83c17d 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -190,6 +190,15 @@ ClientProvider::ClientProvider(const std::string& providerName, THROW_EXCEPTION2(std::invalid_argument, providerName); } +ClientProvider::ClientProvider(const std::tr1::shared_ptr& provider) + :impl(new Impl) +{ + impl->provider = provider; + + if(!impl->provider) + THROW_EXCEPTION2(std::invalid_argument, "null ChannelProvider"); +} + ClientProvider::~ClientProvider() {} ClientChannel diff --git a/src/client/pva/client.h b/src/client/pva/client.h index 32f84b8..ff5df34 100644 --- a/src/client/pva/client.h +++ b/src/client/pva/client.h @@ -365,6 +365,7 @@ public: */ ClientProvider(const std::string& providerName, const std::tr1::shared_ptr& conf = std::tr1::shared_ptr()); + explicit ClientProvider(const std::tr1::shared_ptr& provider); ~ClientProvider(); /** Get a new Channel