/* * testServer.cpp */ #ifdef _WIN32 #define NOMINMAX #endif #include #include #include #include #include #include #include #include #include #include #include #include #include // TODO temp #include "testADCSim.cpp" using namespace epics::pvAccess; using namespace epics::pvData; using namespace std; using std::tr1::static_pointer_cast; using std::tr1::dynamic_pointer_cast; // forward declaration void testServerShutdown(); // TODO temp #include "testNTImage.cpp" Mutex structureStoreMutex; map structureStore; class StructureChangedCallback { public: POINTER_DEFINITIONS(StructureChangedCallback); // TODO for now no BitSets, etc. virtual void structureChanged() = 0; }; typedef map > StructureChangedListenersMap; StructureChangedListenersMap structureChangedListeners; static void notifyStructureChanged(std::string const & name) { // NOTE: not thread-safe if (structureChangedListeners.find(name) != structureChangedListeners.end()) { vector & vec = structureChangedListeners[name]; for (vector::iterator iter = vec.begin(); iter != vec.end(); iter++) { (*iter)->structureChanged(); } } } static PVStructure::shared_pointer getRequestedStructure( PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & pvRequest, string subfieldName = "field") { // if pvRequest is empty, just use pvStructure if (pvRequest.get() && pvRequest->getPVFields().size() > 0) { PVStructure::shared_pointer pvRequestFields; if (pvRequest->getSubField(subfieldName)) pvRequestFields = pvRequest->getStructureField(subfieldName); else pvRequestFields = pvRequest; // if pvRequest is empty, just use pvStructure if (pvRequestFields->getPVFields().size() > 0) { StringArray const & fieldNames = pvRequestFields->getStructure()->getFieldNames(); PVFieldPtrArray pvFields; StringArray actualFieldNames; for (StringArray::const_iterator iter = fieldNames.begin(); iter != fieldNames.end(); iter++) { PVFieldPtr pvField = pvStructure->getSubField(*iter); if (pvField) { PVStructurePtr pvFieldStructure = std::tr1::dynamic_pointer_cast(pvField); PVStructurePtr pvRequestFieldStructure = std::tr1::dynamic_pointer_cast(pvRequestFields->getSubField(*iter)); if (pvRequestFieldStructure->getPVFields().size() > 0 && pvFieldStructure.get()) { // add subfields only actualFieldNames.push_back(*iter); pvFields.push_back(getRequestedStructure(pvFieldStructure, pvRequestFieldStructure)); } else { // add entire field actualFieldNames.push_back(*iter); pvFields.push_back(pvField); } } } return getPVDataCreate()->createPVStructure(actualFieldNames, pvFields); } } return pvStructure; } class ProcessAction : public Runnable { public: typedef vector ChannelProcessVector; ChannelProcessVector toProcess; AtomicBoolean stopped; double period; ProcessAction(double periodHz) : period(periodHz) {} virtual void run() { while (!stopped.get()) { for (ChannelProcessVector::iterator iter = toProcess.begin(); iter != toProcess.end(); iter++) { try { (*iter)->process(); } catch (std::exception &ex) { std::cerr << "Unhandled exception caught in ProcessAction::run(): " << ex.what() << std::endl; } catch (...) { std::cerr << "Unhandled exception caught in ProcessAction::run()" << std::endl; } } epicsThreadSleep(period); } } }; // ADC class ADCAction : public Runnable { public: string name; epics::pvData::PVStructure::shared_pointer adcMatrix; SimADC::smart_pointer_type adcSim; AtomicBoolean stopped; ADCAction() {} virtual void run() { while (!stopped.get()) { if (adcSim->updated.wait(1.0)) { try { // TODO lock structureStoreMutex epicsGuard guard(adcSim->mutex); epicsUInt32 len = adcSim->prev_nSamples; double *val = adcSim->data.value.get(); PVDoubleArrayPtr adcSimArray = adcMatrix->getSubField("value"); PVDoubleArray::svector temp(adcSimArray->reuse()); temp.resize(len); std::copy(val, val + len, temp.begin()); adcSimArray->replace(freeze(temp)); baseValue::shape_t* shape = &adcSim->data.shape; size_t shapeLen = shape->size(); PVIntArrayPtr adcSimShape = adcMatrix->getSubField("dim"); PVIntArray::svector temp2(adcSimShape->reuse()); temp2.resize(shapeLen); std::copy(shape->begin(), shape->end(), temp2.begin()); adcSimShape->replace(freeze(temp2)); PVStructure::shared_pointer ts = adcMatrix->getStructureField("timeStamp"); PVTimeStamp timeStamp; timeStamp.attach(ts); TimeStamp current; current.put(adcSim->X.timeStamp.tv_sec, adcSim->X.timeStamp.tv_nsec); timeStamp.set(current); notifyStructureChanged(name); } catch (std::exception &ex) { std::cerr << "Unhandled exception caught in ADCThread::run(): " << ex.what() << std::endl; } catch (...) { std::cerr << "Unhandled exception caught in ADCThread::run()" << std::endl; } } } } }; // testNTNDArray class NTNDArrayAction : public Runnable { public: string name; PVStructure::shared_pointer pvImage; float angle; double period; AtomicBoolean stopped; NTNDArrayAction(double periodHz) : angle(0), period(periodHz) { } virtual void run() { while (!stopped.get()) { { try { // TODO lock structureStoreMutex // TODO not nice, since we supply original here rotateImage(pvImage, epicsv4_raw, angle); angle += 1; notifyStructureChanged(name); } catch (std::exception &ex) { std::cerr << "Unhandled exception caught in NTNDArrayAction::run(): " << ex.what() << std::endl; } catch (...) { std::cerr << "Unhandled exception caught in NTNDArrayAction::run()" << std::endl; } epicsThreadSleep(period); } } } }; static epics::pvData::PVStructure::shared_pointer createNTTable(int columnsCount, bool timeStamp = false) { StringArray fieldNames(columnsCount); FieldConstPtrArray fields(columnsCount); char sbuf[16]; PVStringArray::svector labels(columnsCount); for (int i = 0; i < columnsCount; i++) { sprintf(sbuf, "column%d", i); fieldNames[i] = sbuf; fields[i] = getFieldCreate()->createScalarArray(pvDouble); labels[i] = sbuf; } Structure::const_shared_pointer valueStructure( getFieldCreate()->createStructure(fieldNames, fields) ); size_t nfields = timeStamp ? 3 : 2; StringArray tableFieldNames(nfields); FieldConstPtrArray tableFields(nfields); tableFieldNames[0] = "labels"; tableFields[0] = getFieldCreate()->createScalarArray(pvString); tableFieldNames[1] = "value"; tableFields[1] = valueStructure; if (timeStamp) { tableFieldNames[2] = "timeStamp"; tableFields[2] = getStandardField()->timeStamp(); } PVStructure::shared_pointer result( getPVDataCreate()->createPVStructure( getFieldCreate()->createStructure( "uri:ev4:nt/2012/pwd:NTTable", tableFieldNames, tableFields) ) ); result->getSubField("labels")->replace(freeze(labels)); return result; } static epics::pvData::PVStructure::shared_pointer createNTNameValue(int columnsCount, bool timeStamp = false) { StringArray fieldNames(columnsCount); FieldConstPtrArray fields(columnsCount); char sbuf[16]; PVStringArray::svector labels(columnsCount); for (int i = 0; i < columnsCount; i++) { sprintf(sbuf, "name%d", i); fieldNames[i] = sbuf; fields[i] = getFieldCreate()->createScalarArray(pvDouble); labels[i] = sbuf; } size_t nfields = timeStamp ? 3 : 2; StringArray tableFieldNames(nfields); FieldConstPtrArray tableFields(nfields); tableFieldNames[0] = "name"; tableFields[0] = getFieldCreate()->createScalarArray(pvString); tableFieldNames[1] = "value"; tableFields[1] = getFieldCreate()->createScalarArray(pvDouble); if (timeStamp) { tableFieldNames[2] = "timeStamp"; tableFields[2] = getStandardField()->timeStamp(); } PVStructure::shared_pointer result( getPVDataCreate()->createPVStructure( getFieldCreate()->createStructure( "uri:ev4:nt/2012/pwd:NTNameValue", tableFieldNames, tableFields) ) ); result->getSubField("name")->replace(freeze(labels)); return result; } static epics::pvData::PVStructure::shared_pointer createNTAggregate() { epics::pvData::StructureConstPtr s = getFieldCreate()->createFieldBuilder()-> setId("uri:ev4:nt/2012/pwd:NTAggregate")-> add("value", pvDouble)-> add("N", pvLong)-> add("dispersion", pvDouble)-> add("first", pvDouble)-> add("firstTimeStamp", getStandardField()->timeStamp())-> add("last", pvDouble)-> add("lastTimeStamp", getStandardField()->timeStamp())-> add("max", pvDouble)-> add("min", pvDouble)-> createStructure(); return getPVDataCreate()->createPVStructure(s); } static epics::pvData::PVStructure::shared_pointer createNTHistogram() { epics::pvData::StructureConstPtr s = getFieldCreate()->createFieldBuilder()-> setId("uri:ev4:nt/2012/pwd:NTHistogram")-> addArray("ranges", pvDouble)-> addArray("value", pvInt)-> add("timeStamp", getStandardField()->timeStamp())-> createStructure(); return getPVDataCreate()->createPVStructure(s); } static void generateNTTableDoubleValues(epics::pvData::PVStructure::shared_pointer result) { PVStringArray::shared_pointer pvLabels = (static_pointer_cast(result->getScalarArrayField("labels", pvString))); PVStringArray::const_svector ld(pvLabels->view()); PVStructure::shared_pointer resultValue = result->getStructureField("value"); #define ROWS 10 double values[ROWS]; #define FILL_VALUES(OFFSET) \ for (int r = 0; r < ROWS; r++) \ values[r] = rand()/((double)RAND_MAX+1) + OFFSET; int offset = 0; for (PVStringArray::const_svector::const_iterator iter = ld.begin(); iter != ld.end(); iter++, offset++) { FILL_VALUES(offset); PVDoubleArray::shared_pointer arr = resultValue->getSubField(*iter); PVDoubleArray::svector temp(arr->reuse()); temp.resize(ROWS); std::copy(values, values + ROWS, temp.begin()); arr->replace(freeze(temp)); } } static void generateNTNameValueDoubleValues(epics::pvData::PVStructure::shared_pointer result) { size_t len = result->getSubField("name")->getLength(); PVDoubleArray::shared_pointer arr = result->getSubField("value"); PVDoubleArray::svector temp(arr->reuse()); temp.resize(len); for (size_t i = 0; i < len; i++) temp[i] = rand()/((double)RAND_MAX+1) + i; arr->replace(freeze(temp)); } static void setTimeStamp(PVStructure::shared_pointer const & ts) { PVTimeStamp timeStamp; timeStamp.attach(ts); TimeStamp current; current.getCurrent(); timeStamp.set(current); } static void generateNTAggregateValues(epics::pvData::PVStructure::shared_pointer result) { setTimeStamp(result->getStructureField("firstTimeStamp")); #define N 1024 double values[N]; for (int r = 0; r < N; r++) values[r] = rand()/((double)RAND_MAX+1); double sum = 0; double min = 1; double max = -1; for (int r = 0; r < N; r++) { sum += values[r]; if (values[r] < min) min = values[r]; if (values[r] > max) max = values[r]; } double avg = sum/N; sum = 0.0; for (int r = 0; r < N; r++) { double t = (values[r] - avg); sum += t*t; } double stddev = sqrt(sum/N); result->getSubField("value")->put(avg); result->getSubField("min")->put(min); result->getSubField("max")->put(max); result->getSubField("first")->put(values[0]); result->getSubField("last")->put(values[N-1]); result->getSubField("dispersion")->put(stddev); result->getSubField("N")->put(N); #undef N setTimeStamp(result->getStructureField("lastTimeStamp")); } static void generateNTHistogramValues(epics::pvData::PVStructure::shared_pointer result) { #define N 100 { PVDoubleArray::shared_pointer arr = result->getSubField("ranges"); PVDoubleArray::svector temp(arr->reuse()); temp.resize(N+1); for (size_t i = 0; i < (N+1); i++) temp[i] = i*10; arr->replace(freeze(temp)); } { PVIntArray::shared_pointer arr = result->getSubField("value"); PVIntArray::svector temp(arr->reuse()); temp.resize(N); for (size_t i = 0; i < N; i++) temp[i] = (int32)((rand()/((double)RAND_MAX+1))*1000); arr->replace(freeze(temp)); } #undef N setTimeStamp(result->getStructureField("timeStamp")); } class ChannelFindRequesterImpl : public ChannelFindRequester { virtual void channelFindResult(epics::pvData::Status const & status, ChannelFind::shared_pointer const & /*channelFind*/, bool wasFound) { std::cout << "[ChannelFindRequesterImpl] channelFindResult(" << status << ", ..., " << wasFound << ")" << std::endl; } }; class ChannelRequesterImpl : public ChannelRequester { virtual string getRequesterName() { return "ChannelRequesterImpl"; }; virtual void message(std::string const & message,MessageType messageType) { std::cout << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl; } virtual void channelCreated(epics::pvData::Status const & /*status*/, Channel::shared_pointer const & /*channel*/) { //std::cout << "channelCreated(" << status << ", " // << (channel ? channel->getChannelName() : "(null)") << ")" << std::endl; } virtual void channelStateChange(Channel::shared_pointer const & /*channel*/, Channel::ConnectionState /*connectionState*/) { //std::cout << "channelStateChange(" << channel->getChannelName() << ", " << Channel::ConnectionStateNames[connectionState] << ")" << std::endl; } }; class ChannelProcessRequesterImpl : public ChannelProcessRequester { //TODO weak ChannelProcess::shared_pointer m_channelProcess; virtual string getRequesterName() { return "ProcessRequesterImpl"; }; virtual void message(std::string const & message,MessageType messageType) { std::cout << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl; } virtual void channelProcessConnect(const epics::pvData::Status& /*status*/, ChannelProcess::shared_pointer const & /*channelProcess*/) { //std::cout << "channelProcessConnect(" << status << ")" << std::endl; //m_channelProcess = channelProcess; } virtual void processDone(const epics::pvData::Status& /*status*/, ChannelProcess::shared_pointer const &) { //std::cout << "processDone(" << status << ")" << std::endl; } }; PVACCESS_REFCOUNT_MONITOR_DEFINE(mockChannelProcess); class MockChannelProcess : public ChannelProcess, public std::tr1::enable_shared_from_this { private: Channel::shared_pointer m_channel; ChannelProcessRequester::shared_pointer m_channelProcessRequester; PVStructure::shared_pointer m_pvStructure; PVScalarPtr m_valueField; PVTimeStamp m_timeStamp; AtomicBoolean m_lastRequest; protected: MockChannelProcess(Channel::shared_pointer const & channel, ChannelProcessRequester::shared_pointer const & channelProcessRequester, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & /*pvRequest*/) : m_channel(channel), m_channelProcessRequester(channelProcessRequester), m_pvStructure(pvStructure) { PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(mockChannelProcess); PVFieldPtr ts = pvStructure->getSubField("timeStamp"); if (ts) m_timeStamp.attach(ts); ChannelProcess::shared_pointer thisPtr; // we return null = static_pointer_cast(shared_from_this()); PVFieldPtr field = pvStructure->getSubField("value"); if (field.get() == 0) { Status noValueFieldStatus(Status::STATUSTYPE_ERROR, "no 'value' field"); m_channelProcessRequester->channelProcessConnect(noValueFieldStatus, thisPtr); // NOTE client must destroy this instance... // do not access any fields and return ASAP return; } if (field->getField()->getType() != scalar) { Status notAScalarStatus(Status::STATUSTYPE_ERROR, "'value' field not scalar type"); m_channelProcessRequester->channelProcessConnect(notAScalarStatus, thisPtr); // NOTE client must destroy this instance... // do not access any fields and return ASAP return; } m_valueField = dynamic_pointer_cast(field); } public: static ChannelProcess::shared_pointer create( Channel::shared_pointer const & channel, ChannelProcessRequester::shared_pointer const & channelProcessRequester, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & pvRequest) { // TODO use std::make_shared std::tr1::shared_ptr tp( new MockChannelProcess(channel, channelProcessRequester, pvStructure, pvRequest) ); ChannelProcess::shared_pointer thisPtr = tp; // TODO pvRequest channelProcessRequester->channelProcessConnect(Status::Ok, thisPtr); return thisPtr; } virtual ~MockChannelProcess() { PVACCESS_REFCOUNT_MONITOR_DESTRUCT(mockChannelProcess); } virtual void process() { { ScopedLock lock(shared_from_this()); if (m_pvStructure->getStructure()->getID() == "uri:ev4:nt/2012/pwd:NTTable") { generateNTTableDoubleValues(m_pvStructure); } else if (m_pvStructure->getStructure()->getID() == "uri:ev4:nt/2012/pwd:NTNameValue") { generateNTNameValueDoubleValues(m_pvStructure); } else if (m_pvStructure->getStructure()->getID() == "uri:ev4:nt/2012/pwd:NTAggregate") { generateNTAggregateValues(m_pvStructure); } else if (m_pvStructure->getStructure()->getID() == "uri:ev4:nt/2012/pwd:NTHistogram") { generateNTHistogramValues(m_pvStructure); } else if (m_pvStructure->getStructure()->getID() == "uri:ev4:test/2014/binaryCounter") { PVBytePtr pvByte = static_pointer_cast(m_valueField); int8 val = pvByte->get() + 1; pvByte->put(val); m_pvStructure->getSubField("bit0")->put(val & (1 << 0)); m_pvStructure->getSubField("bit1")->put(val & (1 << 1)); m_pvStructure->getSubField("bit2")->put(val & (1 << 2)); m_pvStructure->getSubField("bit3")->put(val & (1 << 3)); m_pvStructure->getSubField("bit4")->put(val & (1 << 4)); m_pvStructure->getSubField("bit5")->put(val & (1 << 5)); m_pvStructure->getSubField("bit6")->put(val & (1 << 6)); m_pvStructure->getSubField("bit7")->put(val & (1 << 7)); } else if (m_valueField.get()) { switch (m_valueField->getScalar()->getScalarType()) { case pvBoolean: { // negate PVBooleanPtr pvBoolean = static_pointer_cast(m_valueField); pvBoolean->put(!pvBoolean->get()); break; } case pvByte: { // increment by one PVBytePtr pvByte = static_pointer_cast(m_valueField); pvByte->put(pvByte->get() + 1); break; } case pvShort: { // increment by one PVShortPtr pvShort = static_pointer_cast(m_valueField); pvShort->put(pvShort->get() + 1); break; } case pvInt: { // increment by one PVIntPtr pvInt = static_pointer_cast(m_valueField); pvInt->put(pvInt->get() + 1); break; } case pvLong: { // increment by one PVLongPtr pvLong = static_pointer_cast(m_valueField); pvLong->put(pvLong->get() + 1); break; } case pvFloat: { // increment by one PVFloatPtr pvFloat = static_pointer_cast(m_valueField); pvFloat->put(pvFloat->get() + 1.0f); break; } case pvDouble: { double noise = ((rand()/(double)RAND_MAX)-0.5)*2; // increment by one PVDoublePtr pvDouble = static_pointer_cast(m_valueField); pvDouble->put(pvDouble->get() + noise /*1.0*/); break; } case pvString: { // increment by one PVStringPtr pvString = static_pointer_cast(m_valueField); string val = pvString->get(); if (val.empty()) pvString->put("gen0"); else { char c = val[3]; c++; pvString->put("gen" + string(1, c)); } break; } default: // noop break; } } } if (m_timeStamp.isAttached()) { TimeStamp current; current.getCurrent(); m_timeStamp.set(current); } m_channelProcessRequester->processDone(Status::Ok, shared_from_this()); notifyStructureChanged(m_channel->getChannelName()); if (m_lastRequest.get()) destroy(); } virtual void lastRequest() { m_lastRequest.set(); } virtual Channel::shared_pointer getChannel() { return m_channel; } virtual void cancel() { } virtual void destroy() { } virtual void lock() { structureStoreMutex.lock(); } virtual void unlock() { structureStoreMutex.unlock(); } }; static ChannelProcess::shared_pointer getChannelProcess( Channel::shared_pointer const & channel, PVStructure::shared_pointer const & pvRequest) { PVScalar::shared_pointer pvScalar = pvRequest->getSubField("record._options.process"); if (pvScalar && pvScalar->getAs()) { std::tr1::shared_ptr cpr(new ChannelProcessRequesterImpl()); return channel->createChannelProcess(cpr, PVStructure::shared_pointer()); } else return ChannelProcess::shared_pointer(); } PVACCESS_REFCOUNT_MONITOR_DEFINE(mockChannelGet); class MockChannelGet : public ChannelGet, public StructureChangedCallback, public std::tr1::enable_shared_from_this { private: Channel::shared_pointer m_channel; ChannelGetRequester::shared_pointer m_channelGetRequester; bool m_alwaysSendAll; PVStructure::shared_pointer m_pvStructure; BitSet::shared_pointer m_bitSet; ChannelProcess::shared_pointer m_channelProcess; AtomicBoolean m_changed; AtomicBoolean m_lastRequest; protected: MockChannelGet(Channel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & pvRequest) : m_channel(channel), m_channelGetRequester(channelGetRequester), m_alwaysSendAll(false), m_pvStructure(getRequestedStructure(pvStructure, pvRequest)), m_bitSet(new BitSet(m_pvStructure->getNumberFields())), m_channelProcess(getChannelProcess(channel, pvRequest)) { PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(mockChannelGet); PVScalar::shared_pointer pvScalar = pvRequest->getSubField("record._options.alwaysSendAll"); if (pvScalar) m_alwaysSendAll = pvScalar->getAs(); m_changed.set(); // initial value } public: static ChannelGet::shared_pointer create( Channel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & pvRequest) { // TODO use std::make_shared std::tr1::shared_ptr tp( new MockChannelGet(channel, channelGetRequester, pvStructure, pvRequest) ); ChannelGet::shared_pointer thisPtr = tp; // register structureChangedListeners[channel->getChannelName()].push_back(std::tr1::dynamic_pointer_cast(thisPtr)); channelGetRequester->channelGetConnect(Status::Ok, thisPtr, static_cast(thisPtr.get())->m_pvStructure->getStructure()); return thisPtr; } virtual ~MockChannelGet() { PVACCESS_REFCOUNT_MONITOR_DESTRUCT(mockChannelGet); } virtual void get() { if (m_channelProcess) m_channelProcess->process(); // TODO far from being thread-safe if (m_alwaysSendAll || m_changed.get()) { m_bitSet->set(0); m_changed.clear(); } else m_bitSet->clear(0); m_channelGetRequester->getDone(Status::Ok, shared_from_this(), m_pvStructure, m_bitSet); if (m_lastRequest.get()) destroy(); } virtual void structureChanged() { m_changed.set(); } virtual void lastRequest() { m_lastRequest.set(); } virtual Channel::shared_pointer getChannel() { return m_channel; } virtual void cancel() { } virtual void destroy() { if (m_channelProcess) m_channelProcess->destroy(); // remove itself from listeners table if (structureChangedListeners.count(m_channel->getChannelName())) { vector &vec = structureChangedListeners[m_channel->getChannelName()]; for (vector::iterator i = vec.begin(); i != vec.end(); i++) { if (i->get() == this) { vec.erase(i); break; } } } } virtual void lock() { structureStoreMutex.lock(); } virtual void unlock() { structureStoreMutex.unlock(); } }; PVACCESS_REFCOUNT_MONITOR_DEFINE(mockChannelPut); class MockChannelPut : public ChannelPut, public std::tr1::enable_shared_from_this { private: Channel::shared_pointer m_channel; ChannelPutRequester::shared_pointer m_channelPutRequester; PVStructure::shared_pointer m_pvStructure; BitSet::shared_pointer m_bitSet; ChannelProcess::shared_pointer m_channelProcess; AtomicBoolean m_lastRequest; protected: MockChannelPut(Channel::shared_pointer const & channel, ChannelPutRequester::shared_pointer const & channelPutRequester, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & pvRequest) : m_channel(channel), m_channelPutRequester(channelPutRequester), m_pvStructure(getRequestedStructure(pvStructure, pvRequest)), m_bitSet(new BitSet(m_pvStructure->getNumberFields())), m_channelProcess(getChannelProcess(channel, pvRequest)) { PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(mockChannelPut); m_bitSet->set(0); // TODO } public: static ChannelPut::shared_pointer create( Channel::shared_pointer const & channel, ChannelPutRequester::shared_pointer const & channelPutRequester, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & pvRequest) { // TODO use std::make_shared std::tr1::shared_ptr tp( new MockChannelPut(channel, channelPutRequester, pvStructure, pvRequest) ); ChannelPut::shared_pointer thisPtr = tp; channelPutRequester->channelPutConnect(Status::Ok, thisPtr, static_cast(thisPtr.get())->m_pvStructure->getStructure()); return thisPtr; } virtual ~MockChannelPut() { PVACCESS_REFCOUNT_MONITOR_DESTRUCT(mockChannelPut); } virtual void put(PVStructure::shared_pointer const & pvPutStructure, BitSet::shared_pointer const & putBitSet) { // TODO use putBitSet and do not copy all // (note that server code has already not deserialized fields whose bits are not set) if (putBitSet->cardinality()) { lock(); getConvert()->copy(pvPutStructure, m_pvStructure); unlock(); } if (m_channelProcess) m_channelProcess->process(); m_channelPutRequester->putDone(Status::Ok, shared_from_this()); notifyStructureChanged(m_channel->getChannelName()); if (m_lastRequest.get()) destroy(); } virtual void get() { // NOTE: alwasy returns entire m_bitSet m_channelPutRequester->getDone(Status::Ok, shared_from_this(), m_pvStructure, m_bitSet); if (m_lastRequest.get()) destroy(); } virtual void cancel() { } virtual void lastRequest() { m_lastRequest.set(); } virtual Channel::shared_pointer getChannel() { return m_channel; } virtual void destroy() { if (m_channelProcess) m_channelProcess->destroy(); } virtual void lock() { structureStoreMutex.lock(); } virtual void unlock() { structureStoreMutex.unlock(); } }; PVACCESS_REFCOUNT_MONITOR_DEFINE(mockChannelPutGet); class MockChannelPutGet : public ChannelPutGet, public std::tr1::enable_shared_from_this { private: Channel::shared_pointer m_channel; ChannelPutGetRequester::shared_pointer m_channelPutGetRequester; PVStructure::shared_pointer m_getStructure; BitSet::shared_pointer m_getBitSet; PVStructure::shared_pointer m_putStructure; BitSet::shared_pointer m_putBitSet; ChannelProcess::shared_pointer m_channelProcess; AtomicBoolean m_lastRequest; protected: MockChannelPutGet(Channel::shared_pointer const & channel, ChannelPutGetRequester::shared_pointer const & channelPutGetRequester, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & pvRequest) : m_channel(channel), m_channelPutGetRequester(channelPutGetRequester), m_getStructure(getRequestedStructure(pvStructure, pvRequest, "getField")), m_getBitSet(new BitSet(m_getStructure->getNumberFields())), m_putStructure(getRequestedStructure(pvStructure, pvRequest, "putField")), m_putBitSet(new BitSet(m_putStructure->getNumberFields())), m_channelProcess(getChannelProcess(channel, pvRequest)) { PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(mockChannelPutGet); // always all m_getBitSet->set(0); } public: static ChannelPutGet::shared_pointer create( Channel::shared_pointer const & channel, ChannelPutGetRequester::shared_pointer const & channelPutGetRequester, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & pvRequest) { ChannelPutGet::shared_pointer thisPtr(new MockChannelPutGet(channel, channelPutGetRequester, pvStructure, pvRequest)); channelPutGetRequester->channelPutGetConnect(Status::Ok, thisPtr, static_cast(thisPtr.get())->m_putStructure->getStructure(), static_cast(thisPtr.get())->m_getStructure->getStructure()); return thisPtr; } virtual ~MockChannelPutGet() { PVACCESS_REFCOUNT_MONITOR_DESTRUCT(mockChannelPutGet); } virtual void putGet(PVStructure::shared_pointer const & pvPutStructure, BitSet::shared_pointer const & putBitSet) { // TODO use putBitSet and do not copy all // (note that server code has already not deserialized fields whose bits are not set) if (putBitSet->cardinality()) { lock(); getConvert()->copy(pvPutStructure, m_putStructure); unlock(); } if (m_channelProcess) m_channelProcess->process(); m_channelPutGetRequester->putGetDone(Status::Ok, shared_from_this(), m_getStructure, m_getBitSet); notifyStructureChanged(m_channel->getChannelName()); if (m_lastRequest.get()) destroy(); } virtual void getGet() { m_channelPutGetRequester->getGetDone(Status::Ok, shared_from_this(), m_getStructure, m_getBitSet); if (m_lastRequest.get()) destroy(); } virtual void getPut() { // putGet might mess with bitSet m_putBitSet->clear(); m_putBitSet->set(0); m_channelPutGetRequester->getPutDone(Status::Ok, shared_from_this(), m_putStructure, m_putBitSet); if (m_lastRequest.get()) destroy(); } virtual void lastRequest() { m_lastRequest.set(); } virtual Channel::shared_pointer getChannel() { return m_channel; } virtual void cancel() { } virtual void destroy() { if (m_channelProcess) m_channelProcess->destroy(); } virtual void lock() { structureStoreMutex.lock(); } virtual void unlock() { structureStoreMutex.unlock(); } }; static bool handleHelp( epics::pvData::PVStructure::shared_pointer const & args, ChannelRPC::shared_pointer const & channelRPC, ChannelRPCRequester::shared_pointer const & channelRPCRequester, string const & helpText ) { if (args->getSubField("help")) { StringArray fieldNames(1); FieldConstPtrArray fields(1); fieldNames[0] = "value"; fields[0] = getFieldCreate()->createScalar(pvString); PVStructure::shared_pointer result( getPVDataCreate()->createPVStructure( getFieldCreate()->createStructure( "uri:ev4:nt/2012/pwd:NTScalar", fieldNames, fields) ) ); static_pointer_cast(result->getStringField("value"))->put(helpText); channelRPCRequester->requestDone(Status::Ok, channelRPC, result); return true; } else { return false; } } PVACCESS_REFCOUNT_MONITOR_DEFINE(mockChannelRPC); class MockChannelRPC : public ChannelRPC, public std::tr1::enable_shared_from_this { private: ChannelRPCRequester::shared_pointer m_channelRPCRequester; Channel::shared_pointer m_channel; PVStructure::shared_pointer m_pvStructure; AtomicBoolean m_lastRequest; protected: MockChannelRPC(ChannelRPCRequester::shared_pointer const & channelRPCRequester, Channel::shared_pointer const & channel, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & /*pvRequest*/) : m_channelRPCRequester(channelRPCRequester), m_channel(channel), m_pvStructure(pvStructure) { PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(mockChannelRPC); } public: static ChannelRPC::shared_pointer create(ChannelRPCRequester::shared_pointer const & channelRPCRequester, Channel::shared_pointer const & channel, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & pvRequest) { ChannelRPC::shared_pointer thisPtr(new MockChannelRPC(channelRPCRequester, channel, pvStructure, pvRequest)); // TODO pvRequest channelRPCRequester->channelRPCConnect(Status::Ok, thisPtr); return thisPtr; } virtual ~MockChannelRPC() { PVACCESS_REFCOUNT_MONITOR_DESTRUCT(mockChannelRPC); } virtual void request(epics::pvData::PVStructure::shared_pointer const & pvArgument) { string channelName = m_channel->getChannelName(); if (channelName == "testNTTable") { PVStructure::shared_pointer args( (pvArgument->getStructure()->getID() == "uri:ev4:nt/2012/pwd:NTURI") ? pvArgument->getStructureField("query") : pvArgument ); const string helpText = "Generates a NTTable structure response with 10 rows and a specified number of columns.\n" "Columns are labeled 'column' and values are ' + random [0..1)'.\n" "Arguments:\n\tstring columns\tnumber of table columns\n"; if (handleHelp(args, shared_from_this(), m_channelRPCRequester, helpText)) return; PVStringPtr columns = dynamic_pointer_cast(args->getSubField("columns")); if (columns.get() == 0) { PVStructure::shared_pointer nullPtr; Status errorStatus(Status::STATUSTYPE_ERROR, "no string 'columns' argument specified"); m_channelRPCRequester->requestDone(errorStatus, shared_from_this(), nullPtr); } else { int columnsCount = atoi(columns->get().c_str()); PVStructure::shared_pointer result = createNTTable(columnsCount); generateNTTableDoubleValues(result); m_channelRPCRequester->requestDone(Status::Ok, shared_from_this(), result); } } else if (channelName == "testNTNameValue") { PVStructure::shared_pointer args( (pvArgument->getStructure()->getID() == "uri:ev4:nt/2012/pwd:NTURI") ? pvArgument->getStructureField("query") : pvArgument ); const string helpText = "Generates a NTNameValue structure response with a specified number of columns.\n" "Columns are labeled 'name' and values are ' + random [0..1)'.\n" "Arguments:\n\tstring columns\tnumber of columns\n"; if (handleHelp(args, shared_from_this(), m_channelRPCRequester, helpText)) return; PVStringPtr columns = dynamic_pointer_cast(args->getSubField("columns")); if (columns.get() == 0) { PVStructure::shared_pointer nullPtr; Status errorStatus(Status::STATUSTYPE_ERROR, "no string 'columns' argument specified"); m_channelRPCRequester->requestDone(errorStatus, shared_from_this(), nullPtr); } else { int columnsCount = atoi(columns->get().c_str()); char sbuf[16]; PVStringArray::svector labels(columnsCount); for (int i = 0; i < columnsCount; i++) { sprintf(sbuf, "name%d", i); labels[i] = sbuf; } StringArray tableFieldNames(2); FieldConstPtrArray tableFields(2); tableFieldNames[0] = "name"; tableFields[0] = getFieldCreate()->createScalarArray(pvString); tableFieldNames[1] = "value"; tableFields[1] = getFieldCreate()->createScalarArray(pvDouble); PVStructure::shared_pointer result( getPVDataCreate()->createPVStructure( getFieldCreate()->createStructure( "uri:ev4:nt/2012/pwd:NTNameValue", tableFieldNames, tableFields) ) ); result->getSubField("name")->replace(freeze(labels)); int32 len = columnsCount; PVDoubleArray::svector mv(len); for (int r = 0; r < len; r++) mv[r] = rand()/((double)RAND_MAX+1) + (int)(r); result->getSubField("value")->replace(freeze(mv)); m_channelRPCRequester->requestDone(Status::Ok, shared_from_this(), result); } } else if (channelName == "testNTMatrix") { PVStructure::shared_pointer args( (pvArgument->getStructure()->getID() == "uri:ev4:nt/2012/pwd:NTURI") ? pvArgument->getStructureField("query") : pvArgument ); const string helpText = "Generates a NTMatrix structure response with a specified number of rows and columns.\n" "Matrix values are ' + random [0..1)'.\n" "Arguments:\n" "\tstring rows\tnumber of matrix rows\n" "\tstring columns\tnumber of matrix columns\n" "\t[string bycolumns\torder matrix values in a column-major order]\n"; if (handleHelp(args, shared_from_this(), m_channelRPCRequester, helpText)) return; PVStringPtr rows = dynamic_pointer_cast(args->getSubField("rows")); PVStringPtr columns = dynamic_pointer_cast(args->getSubField("columns")); if (rows.get() == 0 || columns.get() == 0) { PVStructure::shared_pointer nullPtr; Status errorStatus(Status::STATUSTYPE_ERROR, "no string 'rows' and 'columns' arguments specified"); m_channelRPCRequester->requestDone(errorStatus, shared_from_this(), nullPtr); } else { int i = 0; int totalFields = 2; // value[], dim StringArray fieldNames(totalFields); FieldConstPtrArray fields(totalFields); fieldNames[i] = "value"; fields[i++] = getFieldCreate()->createScalarArray(pvDouble); fieldNames[i] = "dim"; fields[i++] = getFieldCreate()->createScalarArray(pvInt); PVStructure::shared_pointer result( getPVDataCreate()->createPVStructure( getFieldCreate()->createStructure("uri:ev4:nt/2012/pwd:NTMatrix", fieldNames, fields) ) ); int32 rowsVal = atoi(rows->get().c_str()); int32 colsVal = atoi(columns->get().c_str()); PVIntArray::svector dimValues(2); dimValues[0] = rowsVal; dimValues[1] = colsVal; result->getSubField("dim")->replace(freeze(dimValues)); PVStringPtr byColumns = static_pointer_cast(args->getSubField("bycolumns")); bool bycolumns = (byColumns.get() && byColumns->get() == "1"); int32 len = rowsVal * colsVal; PVDoubleArray::svector mv(len); for (int r = 0; r < len; r++) if (bycolumns) mv[r] = rand()/((double)RAND_MAX+1) + (int)(r%rowsVal); else mv[r] = rand()/((double)RAND_MAX+1) + (int)(r/colsVal); result->getSubField("value")->replace(freeze(mv)); m_channelRPCRequester->requestDone(Status::Ok, shared_from_this(), result); } } else if (channelName.find("testImage") == 0) { PVStructure::shared_pointer args( (pvArgument->getStructure()->getID() == "uri:ev4:nt/2012/pwd:NTURI") ? pvArgument->getStructureField("query") : pvArgument ); const string helpText = "Generates a NTNDArray structure response that has encoded a specified image.\n" "Arguments:\n" "\tstring file\tfile path (relative to a location where the server was started) of a raw encoded image.\n" "\t\t\tTwo image types are supported:\n" "\t\t\t\t- RGB888 encoded (file extension '.rgb')\n" "\t\t\t\t- 8-bit grayscale encoded (any other extension).\n" "\t\t\tTo generate such encoded images you can use ImageMagick 'convert' tool, e.g.:\n" "\t\t\t\tconvert my_image.png my_image.rgb\n" "\tstring w\timage width\n" "\tstring h\timage height\n"; if (handleHelp(args, shared_from_this(), m_channelRPCRequester, helpText)) return; PVStringPtr file = dynamic_pointer_cast(args->getSubField("file")); PVStringPtr w = dynamic_pointer_cast(args->getSubField("w")); PVStringPtr h = dynamic_pointer_cast(args->getSubField("h")); if (file.get() == 0 || w.get() == 0 || h.get() == 0) { PVStructure::shared_pointer nullPtr; Status errorStatus(Status::STATUSTYPE_ERROR, "not all 'file', 'w' and 'h' arguments specified"); m_channelRPCRequester->requestDone(errorStatus, shared_from_this(), nullPtr); } else { int32 wv = atoi(w->get().c_str()); int32 hv = atoi(h->get().c_str()); string filev = file->get(); // ImageMagick conversion // RGB888: convert img.png img.rgb // grayscale: convert img.png img.g bool isRGB = (filev.find(".rgb") != string::npos); ifstream in(filev.c_str(), ifstream::in | ifstream::binary); if (in.is_open()) { // get file size in.seekg(0, ifstream::end); std::size_t fileSize = in.tellg(); // in case of negative values, etc., this will return right result, however it will fail next check std::size_t expectedSize = wv*hv* (isRGB ? 3 : 1); if (expectedSize == fileSize) { in.seekg(0, ifstream::beg); // TODO sync missing on m_pvStructure if (isRGB) { const int32_t dim[] = { 3, wv, hv }; initImage(m_pvStructure, "", 2 /* NDColorModeRGB1=2 */, 3, dim, fileSize, 0); } else { const int32_t dim[] = { wv, hv }; initImage(m_pvStructure, "", 0 /* NDColorModeMono=0 */, 2, dim, fileSize, 0); } PVByteArrayPtr value = std::tr1::dynamic_pointer_cast(m_pvStructure->getSubField("value")); PVByteArray::svector temp(value->reuse()); temp.resize(fileSize); in.readsome((char*)temp.data(), fileSize); value->replace(freeze(temp)); m_channelRPCRequester->requestDone(Status::Ok, shared_from_this(), m_pvStructure); // for monitors notifyStructureChanged(channelName); } else { PVStructure::shared_pointer nullPtr; Status errorStatus(Status::STATUSTYPE_ERROR, "file size does not match given 'w' and 'h'"); m_channelRPCRequester->requestDone(errorStatus, shared_from_this(), nullPtr); } } else { PVStructure::shared_pointer nullPtr; Status errorStatus(Status::STATUSTYPE_ERROR, "failed to open image file specified"); m_channelRPCRequester->requestDone(errorStatus, shared_from_this(), nullPtr); } } } else if (channelName == "testNTURI") { const string helpText = "Returns the NTURI structure response identical the NTURI request.\n" "Arguments: (none)\n"; if (handleHelp(pvArgument, shared_from_this(), m_channelRPCRequester, helpText)) return; if (pvArgument->getStructure()->getID() != "uri:ev4:nt/2012/pwd:NTURI") { PVStructure::shared_pointer nullPtr; Status errorStatus(Status::STATUSTYPE_ERROR, "argument is not a NTURI structure"); m_channelRPCRequester->requestDone(errorStatus, shared_from_this(), nullPtr); } else { // return argument as result m_channelRPCRequester->requestDone(Status::Ok, shared_from_this(), pvArgument); } } else if (channelName == "testSum") { PVStructure::shared_pointer args( (pvArgument->getStructure()->getID() == "uri:ev4:nt/2012/pwd:NTURI") ? pvArgument->getStructureField("query") : pvArgument ); const string helpText = "Calculates a sum of two integer values.\n" "Arguments:\n" "\tint a\tfirst integer number\n" "\tint b\tsecond integer number\n"; if (handleHelp(args, shared_from_this(), m_channelRPCRequester, helpText)) return; PVInt::shared_pointer pa = args->getSubField("a"); PVInt::shared_pointer pb = args->getSubField("b"); if (!pa || !pb) { PVStructure::shared_pointer nullPtr; Status errorStatus(Status::STATUSTYPE_ERROR, "int a and int b arguments are required"); m_channelRPCRequester->requestDone(errorStatus, shared_from_this(), nullPtr); return; } int a = pa->get(); int b = pb->get(); FieldCreatePtr fieldCreate = getFieldCreate(); StringArray fieldNames; fieldNames.push_back("c"); FieldConstPtrArray fields; fields.push_back(fieldCreate->createScalar(pvInt)); StructureConstPtr resultStructure = fieldCreate->createStructure(fieldNames, fields); PVStructure::shared_pointer result = getPVDataCreate()->createPVStructure(resultStructure); result->getIntField("c")->put(a+b); m_channelRPCRequester->requestDone(Status::Ok, shared_from_this(), result); } else if (channelName.find("testServerShutdown") == 0) { PVStructure::shared_pointer nullPtr; m_channelRPCRequester->requestDone(Status::Ok, shared_from_this(), nullPtr); testServerShutdown(); } else { /* std::string s; pvArgument->toString(&s); std::cout << "RPC" << std::endl << s << std::endl; */ m_channelRPCRequester->requestDone(Status::Ok, shared_from_this(), m_pvStructure); } if (m_lastRequest.get()) destroy(); } virtual void lastRequest() { m_lastRequest.set(); } virtual Channel::shared_pointer getChannel() { return m_channel; } virtual void cancel() { } virtual void destroy() { } virtual void lock() { structureStoreMutex.lock(); } virtual void unlock() { structureStoreMutex.unlock(); } }; PVACCESS_REFCOUNT_MONITOR_DEFINE(mockChannelArray); class MockChannelArray : public ChannelArray, public std::tr1::enable_shared_from_this { private: Channel::shared_pointer m_channel; ChannelArrayRequester::shared_pointer m_channelArrayRequester; PVArray::shared_pointer m_pvArray; PVArray::shared_pointer m_pvStructureArray; AtomicBoolean m_lastRequest; protected: MockChannelArray(Channel::shared_pointer const & channel, ChannelArrayRequester::shared_pointer const & channelArrayRequester, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & /*pvRequest*/) : m_channel(channel), m_channelArrayRequester(channelArrayRequester) { PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(mockChannelArray); m_pvStructureArray = pvStructure->getSubField("value"); if (m_pvStructureArray.get()) m_pvArray = std::tr1::dynamic_pointer_cast( getPVDataCreate()->createPVField(m_pvStructureArray->getField())); } public: static ChannelArray::shared_pointer create(Channel::shared_pointer const & channel, ChannelArrayRequester::shared_pointer const & channelArrayRequester, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & pvRequest) { ChannelArray::shared_pointer thisPtr(new MockChannelArray(channel, channelArrayRequester, pvStructure, pvRequest)); PVArray::shared_pointer array(static_cast(thisPtr.get())->m_pvArray); if (array.get()) channelArrayRequester->channelArrayConnect(Status::Ok, thisPtr, array->getArray()); else { Status errorStatus(Status::STATUSTYPE_ERROR, "no 'value' subfield of array type"); channelArrayRequester->channelArrayConnect(errorStatus, thisPtr, Array::const_shared_pointer()); } return thisPtr; } virtual ~MockChannelArray() { PVACCESS_REFCOUNT_MONITOR_DESTRUCT(mockChannelArray); } template void put(PVArray::shared_pointer const & pvfrom, PVArray::shared_pointer const & pvto, size_t offset, size_t count) { typename APVF::shared_pointer from = std::tr1::static_pointer_cast(pvfrom); typename APVF::shared_pointer to = std::tr1::static_pointer_cast(pvto); typename APVF::const_svector ref(from->view()); if (offset > ref.size()) offset = ref.size(); if (count + offset > ref.size()) count = ref.size() - offset; typename APVF::svector temp(to->reuse()); if (offset + count > temp.size()) temp.resize(offset + count); std::copy(ref.begin(), ref.begin() + count, temp.begin() + offset); to->replace(freeze(temp)); } virtual void putArray(PVArray::shared_pointer const & pvArray, size_t offset, size_t count, size_t stride) { // TODO stride support if (stride == 1) { size_t len = pvArray->getLength(); size_t o = offset < len ? offset : len; size_t c = count; if (c == 0 || ((o + c) > len)) c = len - o; Field::const_shared_pointer field = pvArray->getField(); Type type = field->getType(); if (type == scalarArray) { switch (std::tr1::static_pointer_cast(field)->getElementType()) { case pvBoolean: put(pvArray, m_pvStructureArray, o, c); break; case pvByte: put(pvArray, m_pvStructureArray, o, c); break; case pvShort: put(pvArray, m_pvStructureArray, o, c); break; case pvInt: put(pvArray, m_pvStructureArray, o, c); break; case pvLong: put(pvArray, m_pvStructureArray, o, c); break; case pvUByte: put(pvArray, m_pvStructureArray, o, c); break; case pvUShort: put(pvArray, m_pvStructureArray, o, c); break; case pvUInt: put(pvArray, m_pvStructureArray, o, c); break; case pvULong: put(pvArray, m_pvStructureArray, o, c); break; case pvFloat: put(pvArray, m_pvStructureArray, o, c); break; case pvDouble: put(pvArray, m_pvStructureArray, o, c); break; case pvString: put(pvArray, m_pvStructureArray, o, c); break; } } else if (type == structureArray) put(pvArray, m_pvStructureArray, o, c); else if (type == unionArray) put(pvArray, m_pvStructureArray, o, c); m_channelArrayRequester->putArrayDone(Status::Ok, shared_from_this()); } else { Status notSupported(Status::STATUSTYPE_ERROR, "stride != 1 is not supported"); m_channelArrayRequester->putArrayDone(notSupported, shared_from_this()); } if (m_lastRequest.get()) destroy(); } template void get(PVArray::shared_pointer const & pvfrom, PVArray::shared_pointer const & pvto, size_t offset, size_t count) { typename APVF::shared_pointer from = std::tr1::static_pointer_cast(pvfrom); typename APVF::shared_pointer to = std::tr1::static_pointer_cast(pvto); // TODO range check typename APVF::const_svector temp(from->view()); temp.slice(offset, count); to->replace(temp); } virtual void getArray(size_t offset, size_t count, size_t stride) { // TODO stride support if (stride == 1) { size_t o = offset; if (count == 0) count = m_pvStructureArray->getLength(); size_t c = count; Field::const_shared_pointer field = m_pvArray->getField(); Type type = field->getType(); if (type == scalarArray) { switch (std::tr1::static_pointer_cast(field)->getElementType()) { case pvBoolean: get(m_pvStructureArray, m_pvArray, o, c); break; case pvByte: get(m_pvStructureArray, m_pvArray, o, c); break; case pvShort: get(m_pvStructureArray, m_pvArray, o, c); break; case pvInt: get(m_pvStructureArray, m_pvArray, o, c); break; case pvLong: get(m_pvStructureArray, m_pvArray, o, c); break; case pvUByte: get(m_pvStructureArray, m_pvArray, o, c); break; case pvUShort: get(m_pvStructureArray, m_pvArray, o, c); break; case pvUInt: get(m_pvStructureArray, m_pvArray, o, c); break; case pvULong: get(m_pvStructureArray, m_pvArray, o, c); break; case pvFloat: get(m_pvStructureArray, m_pvArray, o, c); break; case pvDouble: get(m_pvStructureArray, m_pvArray, o, c); break; case pvString: get(m_pvStructureArray, m_pvArray, o, c); break; } } else if (type == structureArray) get(m_pvStructureArray, m_pvArray, o, c); else if (type == unionArray) get(m_pvStructureArray, m_pvArray, o, c); m_channelArrayRequester->getArrayDone(Status::Ok, shared_from_this(), m_pvArray); } else { Status notSupported(Status::STATUSTYPE_ERROR, "stride != 1 is not supported"); m_channelArrayRequester->putArrayDone(notSupported, shared_from_this()); } if (m_lastRequest.get()) destroy(); } virtual void setLength(size_t length) { m_pvStructureArray->setLength(length); m_channelArrayRequester->setLengthDone(Status::Ok, shared_from_this()); if (m_lastRequest.get()) destroy(); } virtual void getLength() { m_channelArrayRequester->getLengthDone(Status::Ok, shared_from_this(), m_pvStructureArray->getLength()); if (m_lastRequest.get()) destroy(); } virtual void lastRequest() { m_lastRequest.set(); } virtual Channel::shared_pointer getChannel() { return m_channel; } virtual void cancel() { } virtual void destroy() { } virtual void lock() { structureStoreMutex.lock(); } virtual void unlock() { structureStoreMutex.unlock(); } }; #include class TraceLog { public: TraceLog(const std::string &method) : m_method(method) { std::cout << "--> " << m_method << std::endl; } ~TraceLog() { std::cout << "<-- " << m_method << std::endl; } private: std::string m_method; } #define TRACE_METHOD() TraceLog trace(CURRENT_FUNCTION); PVACCESS_REFCOUNT_MONITOR_DEFINE(mockMonitor); class MockMonitor : public Monitor, public StructureChangedCallback, public std::tr1::enable_shared_from_this { private: string m_channelName; MonitorRequester::shared_pointer m_monitorRequester; bool m_continuous; PVStructure::shared_pointer m_pvStructure; PVStructure::shared_pointer m_copy; BitSet::shared_pointer m_changedBitSet; BitSet::shared_pointer m_overrunBitSet; Mutex m_lock; enum QueueState { MM_STATE_FULL, MM_STATE_TAKEN, MM_STATE_FREE }; QueueState m_state ; AtomicBoolean m_active; MonitorElement::shared_pointer m_thisPtr; MonitorElement::shared_pointer m_nullMonitor; protected: MockMonitor(std::string const & channelName, MonitorRequester::shared_pointer const & monitorRequester, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & pvRequest) : m_channelName(channelName), m_monitorRequester(monitorRequester), m_continuous(false), m_pvStructure(getRequestedStructure(pvStructure, pvRequest)), m_copy(getPVDataCreate()->createPVStructure(m_pvStructure->getStructure())), m_changedBitSet(new BitSet(m_pvStructure->getNumberFields())), m_overrunBitSet(new BitSet(m_pvStructure->getNumberFields())), m_lock(), m_state(MM_STATE_FREE), m_thisPtr(new MonitorElement()) { PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(mockMonitor); PVScalar::shared_pointer pvScalar = pvRequest->getSubField("record._options.velocious"); if (pvScalar) m_continuous = pvScalar->getAs(); // we always send all m_changedBitSet->set(0); m_thisPtr->pvStructurePtr = m_copy; m_thisPtr->changedBitSet = m_changedBitSet; m_thisPtr->overrunBitSet = m_overrunBitSet; } public: static Monitor::shared_pointer create(std::string const & channelName, MonitorRequester::shared_pointer const & monitorRequester, PVStructure::shared_pointer const & pvStructure, PVStructure::shared_pointer const & pvRequest) { // TODO use std::make_shared std::tr1::shared_ptr tp( new MockMonitor(channelName, monitorRequester, pvStructure, pvRequest) ); Monitor::shared_pointer thisPtr = tp; // register structureChangedListeners[channelName].push_back(std::tr1::dynamic_pointer_cast(thisPtr)); StructureConstPtr structurePtr = static_cast(thisPtr.get())->m_pvStructure->getStructure(); monitorRequester->monitorConnect(Status::Ok, thisPtr, structurePtr); return thisPtr; } virtual ~MockMonitor() { PVACCESS_REFCOUNT_MONITOR_DESTRUCT(mockMonitor); } void copy() { { lock(); getConvert()->copyStructure(m_pvStructure, m_copy); unlock(); } } virtual Status start() { //TRACE_METHOD(); { Lock xx(m_lock); m_state = MM_STATE_FULL; copy(); } // first monitor Monitor::shared_pointer thisPtr = shared_from_this(); m_monitorRequester->monitorEvent(thisPtr); m_active.set(); // set here not to have race condition on first monitor return Status::Ok; } virtual Status stop() { m_active.clear(); return Status::Ok; } virtual void structureChanged() { //TRACE_METHOD(); if (m_active.get()) { { Lock xx(m_lock); if (m_state == MM_STATE_FULL || m_state == MM_STATE_TAKEN) // "queue" full { m_overrunBitSet->set(0); copy(); return; } else { m_overrunBitSet->clear(0); m_state = MM_STATE_FULL; copy(); } } Monitor::shared_pointer thisPtr = shared_from_this(); m_monitorRequester->monitorEvent(thisPtr); } } virtual MonitorElement::shared_pointer poll() { //TRACE_METHOD(); Lock xx(m_lock); if (m_state != MM_STATE_FULL) { return m_nullMonitor; } else { m_state = MM_STATE_TAKEN; return m_thisPtr; } } virtual void release(MonitorElement::shared_pointer const & /*monitorElement*/) { //TRACE_METHOD(); Lock xx(m_lock); if (m_state == MM_STATE_TAKEN) { if (m_continuous) m_state = MM_STATE_FULL; else m_state = MM_STATE_FREE; } } virtual void cancel() { } virtual void destroy() { stop(); // remove itself from listeners table if (structureChangedListeners.count(m_channelName)) { vector &vec = structureChangedListeners[m_channelName]; for (vector::iterator i = vec.begin(); i != vec.end(); i++) { if (i->get() == this) { vec.erase(i); break; } } } } virtual void lock() { structureStoreMutex.lock(); } virtual void unlock() { structureStoreMutex.unlock(); } }; PVACCESS_REFCOUNT_MONITOR_DEFINE(mockChannel); class MockChannel : public Channel, public std::tr1::enable_shared_from_this { private: ChannelProvider::weak_pointer m_provider; ChannelRequester::shared_pointer m_requester; string m_name; string m_remoteAddress; public: // TODO PVStructure::shared_pointer m_pvStructure; protected: MockChannel( ChannelProvider::shared_pointer provider, ChannelRequester::shared_pointer requester, string name, string remoteAddress) : m_provider(provider), m_requester(requester), m_name(name), m_remoteAddress(remoteAddress), m_pvStructure() { PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(mockChannel); if (structureStore.find(m_name) != structureStore.end()) m_pvStructure = structureStore[m_name]; else { // create structure if (m_name.find("testArray") == 0) { string allProperties(""); // string allProperties("alarm,timeStamp,display,control"); m_pvStructure = getStandardPVField()->scalarArray(pvDouble,allProperties); PVDoubleArrayPtr pvField = static_pointer_cast(m_pvStructure->getScalarArrayField(std::string("value"), pvDouble)); int specCount = 0; char postfix[64]; int done = sscanf(m_name.c_str(), "testArray%d%s", &specCount, postfix); if (done && specCount > 0) { PVDoubleArray::svector temp(pvField->reuse()); temp.resize(specCount); double v = 0; for (int n = 0; n < specCount; n++) { temp[n] = v; v+=1; } pvField->replace(freeze(temp)); } else { PVDoubleArray::svector temp(pvField->reuse()); specCount = 1024*1024; temp.resize(specCount); double v = 0; for (int n = 0; n < specCount; n++) { temp[n] = v; v+=1.1; } pvField->replace(freeze(temp)); } /* printf("array prepared------------------------------------!!!\n"); string str; pvField->toString(&str); printf("%s\n", str.c_str()); printf("=============------------------------------------!!!\n"); */ } else if (m_name.find("testMP") == 0 || m_name.find("testImage") == 0) { m_pvStructure = getPVDataCreate()->createPVStructure(createNTNDArrayStructure()); initImageEPICSv4GrayscaleLogo(m_pvStructure); } else if (m_name.find("testTable") == 0) { m_pvStructure = createNTTable(5, true); // 5 columns w/ timeStamp generateNTTableDoubleValues(m_pvStructure); } else if (m_name.find("testNameValue") == 0) { m_pvStructure = createNTNameValue(5, true); // 5 columns w/ timeStamp generateNTNameValueDoubleValues(m_pvStructure); } else if (m_name.find("testAggregate") == 0) { m_pvStructure = createNTAggregate(); generateNTAggregateValues(m_pvStructure); } else if (m_name.find("testHistogram") == 0) { m_pvStructure = createNTHistogram(); generateNTHistogramValues(m_pvStructure); } else if (m_name.find("testADC") == 0) { int i = 0; int totalFields = 6; StringArray fieldNames(totalFields); FieldConstPtrArray fields(totalFields); fieldNames[i] = "value"; fields[i++] = getFieldCreate()->createScalarArray(pvDouble); fieldNames[i] = "dim"; fields[i++] = getFieldCreate()->createScalarArray(pvInt); fieldNames[i] = "descriptor"; fields[i++] = getFieldCreate()->createScalar(pvString); fieldNames[i] = "timeStamp"; fields[i++] = getStandardField()->timeStamp(); fieldNames[i] = "alarm"; fields[i++] = getStandardField()->alarm(); fieldNames[i] = "display"; fields[i++] = getStandardField()->display(); m_pvStructure = getPVDataCreate()->createPVStructure( getFieldCreate()->createStructure("uri:ev4:nt/2012/pwd:NTMatrix", fieldNames, fields) ); // fill with default values PVIntArray::svector dimValue(1); dimValue[0] = 0; m_pvStructure->getSubField("dim")->replace(freeze(dimValue)); m_pvStructure->getStringField("descriptor")->put("Simulated ADC that provides NTMatrix value"); PVStructurePtr displayStructure = m_pvStructure->getStructureField("display"); displayStructure->getDoubleField("limitLow")->put(-1.0); displayStructure->getDoubleField("limitHigh")->put(1.0); displayStructure->getStringField("description")->put("Simulated ADC"); displayStructure->getStringField("format")->put("%f"); displayStructure->getStringField("units")->put("V"); } else if (m_name.find("testRPC") == 0 || m_name == "testNTTable" || m_name == "testNTMatrix") { StringArray fieldNames; PVFieldPtrArray fields; m_pvStructure = getPVDataCreate()->createPVStructure(fieldNames, fields); } else if (m_name.find("testValueOnly") == 0) { string allProperties(""); m_pvStructure = getStandardPVField()->scalar(pvDouble,allProperties); } else if (m_name == "testCounter" || m_name == "testSimpleCounter") { string allProperties("timeStamp"); m_pvStructure = getStandardPVField()->scalar(pvInt,allProperties); } else if (m_name == "testBinaryCounter" ) { epics::pvData::StructureConstPtr s = getFieldCreate()->createFieldBuilder()-> setId("uri:ev4:test/2014/binaryCounter")-> add("value", pvByte)-> add("bit0", pvBoolean)-> add("bit1", pvBoolean)-> add("bit2", pvBoolean)-> add("bit3", pvBoolean)-> add("bit4", pvBoolean)-> add("bit5", pvBoolean)-> add("bit6", pvBoolean)-> add("bit7", pvBoolean)-> add("timeStamp", getStandardField()->timeStamp())-> createStructure(); m_pvStructure = getPVDataCreate()->createPVStructure(s); } else if (m_name == "testEnum") { StringArray choices; choices.push_back("zeroValue"); choices.push_back("oneValue"); choices.push_back("twoValue"); choices.push_back("threeValue"); choices.push_back("fourValue"); choices.push_back("fiveValue"); choices.push_back("sixValue"); choices.push_back("sevenValue"); string allProperties("timeStamp"); m_pvStructure = getStandardPVField()->enumerated(choices,allProperties); } else if (m_name == "testBoundedString" ) { epics::pvData::StructureConstPtr s = getFieldCreate()->createFieldBuilder()-> addBoundedString("value", 8)-> add("timeStamp", getStandardField()->timeStamp())-> createStructure(); m_pvStructure = getPVDataCreate()->createPVStructure(s); } else if (m_name == "testBoundedArray" ) { epics::pvData::StructureConstPtr s = getFieldCreate()->createFieldBuilder()-> addBoundedArray("value", pvDouble, 8)-> add("timeStamp", getStandardField()->timeStamp())-> createStructure(); m_pvStructure = getPVDataCreate()->createPVStructure(s); } else if (m_name == "testFixedArray" ) { epics::pvData::StructureConstPtr s = getFieldCreate()->createFieldBuilder()-> addFixedArray("value", pvDouble, 8)-> add("timeStamp", getStandardField()->timeStamp())-> createStructure(); m_pvStructure = getPVDataCreate()->createPVStructure(s); m_pvStructure->getSubField("value")->setLength(8); } else { string allProperties("alarm,timeStamp,display,control,valueAlarm"); m_pvStructure = getStandardPVField()->scalar(pvDouble,allProperties); //PVDoublePtr pvField = m_pvStructure->getDoubleField(std::string("value")); //pvField->put(1.123); } structureStore[m_name] = m_pvStructure; } } public: static Channel::shared_pointer create( ChannelProvider::shared_pointer provider, ChannelRequester::shared_pointer requester, string name, string remoteAddress) { // TODO use std::make_shared std::tr1::shared_ptr tp( new MockChannel(provider, requester, name, remoteAddress) ); Channel::shared_pointer channelPtr = tp; // already connected, report state requester->channelStateChange(channelPtr, CONNECTED); return channelPtr; } virtual ~MockChannel() { PVACCESS_REFCOUNT_MONITOR_DESTRUCT(mockChannel); } virtual void destroy() { }; virtual string getRequesterName() { return getChannelName(); }; virtual void message(std::string const & message,MessageType messageType) { std::cout << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl; } virtual ChannelProvider::shared_pointer getProvider() { return m_provider.lock(); } virtual std::string getRemoteAddress() { return m_remoteAddress; } virtual std::string getChannelName() { return m_name; } virtual std::tr1::shared_ptr getChannelRequester() { return m_requester; } virtual ConnectionState getConnectionState() { return CONNECTED; } virtual bool isConnected() { return getConnectionState() == CONNECTED; } virtual AccessRights getAccessRights(epics::pvData::PVField::shared_pointer const & /*pvField*/) { return readWrite; } virtual void getField(GetFieldRequester::shared_pointer const & requester,std::string const & subField) { PVFieldPtr pvField; if(subField == "") { pvField = m_pvStructure; } else { pvField = m_pvStructure->getSubField(subField); } if(!pvField.get()) { string errMsg = "field '" + subField + "' not found"; FieldConstPtr nullPtr; Status errorStatus(Status::STATUSTYPE_ERROR, errMsg); requester->getDone(errorStatus,nullPtr); return; } FieldConstPtr fieldPtr = pvField->getField(); requester->getDone(Status::Ok, fieldPtr); } virtual ChannelProcess::shared_pointer createChannelProcess( ChannelProcessRequester::shared_pointer const & channelProcessRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { return MockChannelProcess::create(shared_from_this(), channelProcessRequester, m_pvStructure, pvRequest); } virtual ChannelGet::shared_pointer createChannelGet( ChannelGetRequester::shared_pointer const & channelGetRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { return MockChannelGet::create(shared_from_this(), channelGetRequester, m_pvStructure, pvRequest); } virtual ChannelPut::shared_pointer createChannelPut( ChannelPutRequester::shared_pointer const & channelPutRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { return MockChannelPut::create(shared_from_this(), channelPutRequester, m_pvStructure, pvRequest); } virtual ChannelPutGet::shared_pointer createChannelPutGet( ChannelPutGetRequester::shared_pointer const & channelPutGetRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { return MockChannelPutGet::create(shared_from_this(), channelPutGetRequester, m_pvStructure, pvRequest); } virtual ChannelRPC::shared_pointer createChannelRPC(ChannelRPCRequester::shared_pointer const & channelRPCRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { return MockChannelRPC::create(channelRPCRequester, shared_from_this(), m_pvStructure, pvRequest); } virtual epics::pvData::Monitor::shared_pointer createMonitor( epics::pvData::MonitorRequester::shared_pointer const & monitorRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { return MockMonitor::create(m_name, monitorRequester, m_pvStructure, pvRequest); } virtual ChannelArray::shared_pointer createChannelArray( ChannelArrayRequester::shared_pointer const & channelArrayRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { return MockChannelArray::create(shared_from_this(), channelArrayRequester, m_pvStructure, pvRequest); } virtual void printInfo() { printInfo(std::cout); } virtual void printInfo(std::ostream& out) { out << "CHANNEL : " << getChannelName() << std::endl; ConnectionState state = getConnectionState(); out << "STATE : " << ConnectionStateNames[state] << std::endl; if (state == CONNECTED) { out << "ADDRESS : " << getRemoteAddress() << std::endl; //out << "RIGHTS : " << getAccessRights() << std::endl; } } }; class MockServerChannelProvider; class MockChannelFind : public ChannelFind { public: typedef std::tr1::shared_ptr shared_pointer; typedef std::tr1::shared_ptr const_shared_pointer; MockChannelFind(ChannelProvider::shared_pointer &provider) : m_provider(provider) { } virtual ~MockChannelFind() {} virtual void destroy() { // one instance for all, do not delete at all } virtual ChannelProvider::shared_pointer getChannelProvider() { return m_provider.lock(); }; virtual void cancel() { throw std::runtime_error("not supported"); } private: ChannelProvider::weak_pointer m_provider; }; class MockServerChannelProvider : public ChannelProvider, public std::tr1::enable_shared_from_this { public: typedef std::tr1::shared_ptr shared_pointer; typedef std::tr1::shared_ptr const_shared_pointer; static string PROVIDER_NAME; MockServerChannelProvider() : m_mockChannelFind(), m_counterChannel(), m_adcChannel(), m_mpChannel(), m_scan1Hz(1.0), m_scan1HzThread(), m_scan10Hz(0.1), m_scan10HzThread(), m_adcAction(), m_adcThread(), m_imgAction(0.1), m_imgThread() { } virtual ~MockServerChannelProvider() { m_scan1Hz.stopped.set(); m_scan10Hz.stopped.set(); m_adcAction.stopped.set(); m_imgAction.stopped.set(); } void initialize() { ChannelProvider::shared_pointer chProviderPtr = shared_from_this(); m_mockChannelFind.reset(new MockChannelFind(chProviderPtr)); std::tr1::shared_ptr cr(new ChannelRequesterImpl()); m_counterChannel = MockChannel::create(chProviderPtr, cr, "testCounter", "local"); std::tr1::shared_ptr cpr(new ChannelProcessRequesterImpl()); ChannelProcess::shared_pointer process = m_counterChannel->createChannelProcess(cpr, PVStructure::shared_pointer()); m_scan1Hz.toProcess.push_back(process); Channel::shared_pointer c = MockChannel::create(chProviderPtr, cr, "testRandom", "local"); process = c->createChannelProcess(cpr, PVStructure::shared_pointer()); m_scan10Hz.toProcess.push_back(process); c = MockChannel::create(chProviderPtr, cr, "testTable", "local"); process = c->createChannelProcess(cpr, PVStructure::shared_pointer()); m_scan1Hz.toProcess.push_back(process); c = MockChannel::create(chProviderPtr, cr, "testNameValue", "local"); process = c->createChannelProcess(cpr, PVStructure::shared_pointer()); m_scan1Hz.toProcess.push_back(process); c = MockChannel::create(chProviderPtr, cr, "testAggregate", "local"); process = c->createChannelProcess(cpr, PVStructure::shared_pointer()); m_scan1Hz.toProcess.push_back(process); c = MockChannel::create(chProviderPtr, cr, "testHistogram", "local"); process = c->createChannelProcess(cpr, PVStructure::shared_pointer()); m_scan1Hz.toProcess.push_back(process); c = MockChannel::create(chProviderPtr, cr, "testBinaryCounter", "local"); process = c->createChannelProcess(cpr, PVStructure::shared_pointer()); m_scan1Hz.toProcess.push_back(process); m_scan1HzThread.reset(new epics::pvData::Thread("process1hz", highPriority, &m_scan1Hz)); m_scan10HzThread.reset(new epics::pvData::Thread("process10hz", highPriority, &m_scan10Hz)); m_adcChannel = MockChannel::create(chProviderPtr, cr, "testADC", "local"); m_adcAction.name = "testADC"; m_adcAction.adcMatrix = static_pointer_cast(m_adcChannel)->m_pvStructure; m_adcAction.adcSim = createSimADC("testADC"); m_adcThread.reset(new epics::pvData::Thread("adcThread", highPriority, &m_adcAction)); m_mpChannel = MockChannel::create(chProviderPtr, cr, "testMP", "local"); m_imgAction.name = "testMP"; m_imgAction.pvImage = static_pointer_cast(m_mpChannel)->m_pvStructure; m_imgThread.reset(new epics::pvData::Thread("imgThread", highPriority, &m_imgAction)); } virtual std::string getProviderName() { return PROVIDER_NAME; } virtual void destroy() { } virtual ChannelFind::shared_pointer channelFind( std::string const & channelName, ChannelFindRequester::shared_pointer const & channelFindRequester) { // channel that starts with "test" always exists bool exists = (channelName.find("test") == 0); channelFindRequester->channelFindResult(Status::Ok, m_mockChannelFind, exists); return m_mockChannelFind; } virtual ChannelFind::shared_pointer channelList( ChannelListRequester::shared_pointer const & channelListRequester) { if (!channelListRequester.get()) throw std::runtime_error("null requester"); // NOTE: this adds only active channels, not all (especially RPC ones) PVStringArray::svector channelNames; { Lock guard(structureStoreMutex); channelNames.reserve(structureStore.size()); for (map::const_iterator iter = structureStore.begin(); iter != structureStore.end(); iter++) channelNames.push_back(iter->first); } channelListRequester->channelListResult(Status::Ok, m_mockChannelFind, freeze(channelNames), true); return m_mockChannelFind; } virtual Channel::shared_pointer createChannel( std::string const & channelName, ChannelRequester::shared_pointer const & channelRequester, short priority) { return createChannel(channelName, channelRequester, priority, "local"); } virtual Channel::shared_pointer createChannel( std::string const & channelName, ChannelRequester::shared_pointer const & channelRequester, short /*priority*/, std::string const & address) { if (address == "local") { if (channelName == "testCounter") { channelRequester->channelCreated(Status::Ok, m_counterChannel); return m_counterChannel; } else if (channelName == "testADC") { channelRequester->channelCreated(Status::Ok, m_adcChannel); return m_adcChannel; } else if (channelName == "testMP") { channelRequester->channelCreated(Status::Ok, m_mpChannel); return m_mpChannel; } else { ChannelProvider::shared_pointer chProviderPtr = shared_from_this(); Channel::shared_pointer channel = MockChannel::create(chProviderPtr, channelRequester, channelName, address); channelRequester->channelCreated(Status::Ok, channel); return channel; } } else { Channel::shared_pointer nullPtr; Status errorStatus(Status::STATUSTYPE_ERROR, "only local supported"); channelRequester->channelCreated(errorStatus, nullPtr); return nullPtr; } } private: ChannelFind::shared_pointer m_mockChannelFind; Channel::shared_pointer m_counterChannel; Channel::shared_pointer m_adcChannel; Channel::shared_pointer m_mpChannel; ProcessAction m_scan1Hz; auto_ptr m_scan1HzThread; ProcessAction m_scan10Hz; auto_ptr m_scan10HzThread; ADCAction m_adcAction; auto_ptr m_adcThread; NTNDArrayAction m_imgAction; auto_ptr m_imgThread; }; string MockServerChannelProvider::PROVIDER_NAME = "local"; class MockChannelProviderFactory : public ChannelProviderFactory { public: POINTER_DEFINITIONS(MockChannelProviderFactory); virtual std::string getFactoryName() { return MockServerChannelProvider::PROVIDER_NAME; } virtual ChannelProvider::shared_pointer sharedInstance() { // no shared instance support for mock... return newInstance(); } virtual ChannelProvider::shared_pointer newInstance() { MockServerChannelProvider::shared_pointer channelProvider(new MockServerChannelProvider()); channelProvider->initialize(); return channelProvider; } }; static ServerContextImpl::shared_pointer ctx; void testServer(int timeToRun) { MockChannelProviderFactory::shared_pointer factory(new MockChannelProviderFactory()); registerChannelProviderFactory(factory); //ServerContextImpl::shared_pointer ctx = ServerContextImpl::create(); ctx = ServerContextImpl::create(); ctx->initialize(getChannelProviderRegistry()); ctx->printInfo(); ctx->run(timeToRun); ctx->destroy(); unregisterChannelProviderFactory(factory); structureChangedListeners.clear(); { Lock guard(structureStoreMutex); structureStore.clear(); } ctx.reset(); unregisterChannelProviderFactory(factory); shutdownSimADCs(); } void testServerShutdown() { // NOTE: this is not thread-safe TODO ctx->shutdown(); } #include void usage (char *argv[]) { fprintf (stderr, "\nUsage: %s [options]\n\n" " -h: Help: Print this message\n" "\noptions:\n" " -t : Time to run in seconds, 0 for forever\n" " -d: Enable debug output\n" " -c: Wait for clean shutdown and report used instance count (for expert users)" "\n\n", argv[0]); } #ifndef TESTSERVERNOMAIN int main(int argc, char *argv[]) { int opt; /* getopt() current option */ bool debug = false; bool cleanupAndReport = false; int timeToRun = 0; setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ while ((opt = getopt(argc, argv, ":ht:dc")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(argv); return 0; case 't': /* Print usage */ timeToRun = atoi(optarg); break; case 'd': /* Debug log level */ debug = true; break; case 'c': /* Clean-up and report used instance count */ cleanupAndReport = true; break; case '?': fprintf(stderr, "Unrecognized option: '-%c'. ('%s -h' for help.)\n", optopt, argv[0]); return 1; case ':': fprintf(stderr, "Option '-%c' requires an argument. ('%s -h' for help.)\n", optopt, argv[0]); return 1; default : usage(argv); return 1; } } SET_LOG_LEVEL(debug ? logLevelDebug : logLevelError); srand ( time(NULL) ); testServer(timeToRun); cout << "Done" << endl; if (cleanupAndReport) { // TODO implement wait on context epicsThreadSleep ( 3.0 ); //std::cout << "-----------------------------------------------------------------------" << std::endl; //epicsExitCallAtExits(); } return (0); } #endif