diff --git a/testApp/remote/testADCSim.cpp b/testApp/remote/testADCSim.cpp new file mode 100644 index 0000000..396767a --- /dev/null +++ b/testApp/remote/testADCSim.cpp @@ -0,0 +1,367 @@ +#ifndef DBVALUE_H +#define DBVALUE_H + +#include +#include + +#include +#include +#include +#include + +struct baseValue { + bool remoteWritable; + + typedef std::vector shape_t; + shape_t shape; + + std::string format; + + unsigned short severity; + std::string message; + + struct timespec timeStamp; + + // static meta-data + + std::string egu; + int precision; + + baseValue(); +}; + +template +struct scalarNumericValue : public baseValue { + typedef PVT value_type; + typedef PVT element_type; + + value_type value; + + value_type displayHigh, displayLow; + value_type warnHigh, warnLow; + value_type alarmHigh, alarmLow; + + value_type ctrlHigh, ctrlLow; + value_type step; + + scalarNumericValue(); +}; + +struct scalarEnumValue : public baseValue { + typedef epicsUInt32 value_type; + typedef epicsUInt32 element_type; + + value_type value; + + std::vector choices; + + scalarEnumValue() : value(0) {} +}; + +struct scalarStringValue : public baseValue { + typedef std::string value_type; + typedef std::string element_type; + + value_type value; + + std::vector choices; +}; + +template +struct vectorNumericValue : public baseValue { + typedef std::tr1::shared_ptr value_type; + typedef PVT element_type; + + value_type value; + + element_type displayHigh, displayLow; + + vectorNumericValue() : displayHigh(0), displayLow(0) {} +}; + + + +template +scalarNumericValue::scalarNumericValue() + :baseValue() + ,value(0) + ,displayHigh(0) + ,displayLow(0) + ,warnHigh(0) + ,warnLow(0) + ,alarmHigh(0) + ,alarmLow(0) + ,ctrlHigh(0) + ,ctrlLow(0) + ,step(1) +{} + + +#endif // DBVALUE_H + + + +baseValue::baseValue() + :remoteWritable(true) + ,severity(0) + ,message() + ,precision(0) +{ + timeStamp.tv_sec = timeStamp.tv_nsec = 0; +} + + +#ifndef SIM_H +#define SIM_H + +#include + +#include +#include + +struct SimADC : public std::tr1::enable_shared_from_this, + public epicsThreadRunable +{ + typedef std::tr1::shared_ptr smart_pointer_type; + + epicsMutex mutex; + + scalarNumericValue mult, shift, offset, freq, rate; + + scalarNumericValue nSamples; + epicsUInt32 prev_nSamples; + + scalarEnumValue operation; + + vectorNumericValue data; + + vectorNumericValue X; + + epicsThread runner; + bool runner_stop; + epicsEvent updated; + + SimADC(); + virtual ~SimADC(); + + virtual void run (); + void cycle(); +}; + +SimADC::smart_pointer_type createSimADC(const std::string& name); +void shutdownSimADCs(); + +SimADC::smart_pointer_type getSimADC(const std::string& name); + + +#endif // SIM_H + + + +#include +#include +#include +#include +#include + +#include +#include +#include + + +// 180 / PI +#define RAD2DEG 57.2957795131 + +// 100 / (2*PI) +#define RAD2PCT 15.9154943092 + +struct sim_global_type { + typedef epicsGuard guard_t; + typedef epicsGuardRelease unguard_t; + epicsMutex lock; + typedef std::map sims_t; + sims_t sims; +}; + +static +sim_global_type *sim_global = 0; + +static +void sim_global_init(void*) +{ + try { + sim_global = new sim_global_type; + } catch(...) { + abort(); + } +} + +static +epicsThreadOnceId sim_mute_once = EPICS_THREAD_ONCE_INIT; + + +SimADC::SimADC() + :runner(*this, "Runner", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityMedium) + ,runner_stop(false) +{ + mult.value= rate.value= 1.0; + shift.value= offset.value= 0.0; + freq.value=90.0; + + offset.egu = mult.egu = "V"; + rate.egu = "Hz"; + rate.displayLow = rate.ctrlLow = 0.0; + rate.displayHigh = rate.ctrlHigh = 1e6; + + shift.displayLow = shift.ctrlLow = 0.0; + shift.displayHigh = shift.ctrlHigh = 99.999999; + shift.egu = "%"; + + freq.egu = "deg/pt"; + + + nSamples.value = 10; + prev_nSamples = 0; + cycle(); + + operation.choices.resize(2); + operation.choices[0] = "Stop"; + operation.choices[1] = "Run"; + operation.value = 0; + + data.remoteWritable = false; + X.remoteWritable = false; + + runner.start(); +} + +SimADC::~SimADC() +{ + { + sim_global_type::guard_t G(mutex); + runner_stop = true; + } + runner.exitWait(); +} + +namespace { + template + struct Freeme { + void operator()(T* p) {free(p);} + }; +} + +void SimADC::cycle() +{ + epicsTime now = epicsTime::getCurrent(); + + if(nSamples.value != prev_nSamples) { + X.value.reset(); + data.value.reset(); + prev_nSamples = nSamples.value; + } + + if(!X.value) { + X.value.reset((unsigned int*)malloc(sizeof(epicsUInt32)*prev_nSamples), Freeme()); + unsigned int *val = X.value.get(); + for(size_t i=0; i()); + + X.timeStamp = now; + + if(!X.value) { + X.severity = 3; + X.message = "Alloc fails"; + } + if(!data.value) { + data.severity = 3; + data.message = "Alloc fails"; + return; + } + + double *val = data.value.get(); + for(size_t i=0; i0.0 ? 1.0/rate.value : min_sleep; + sim_global_type::unguard_t U(G); + epicsThreadSleep(zzz); + } + if(runner_stop) + break; + + cycle(); + } + printf("SimADC shutdown\n"); +} + +SimADC::smart_pointer_type createSimADC(const std::string& name) +{ + epicsThreadOnce(&sim_mute_once, &sim_global_init, 0); + + sim_global_type::guard_t G(sim_global->lock); + + sim_global_type::sims_t &sims = sim_global->sims; + + SimADC::smart_pointer_type P(new SimADC); + + sims[name] = P; + + return P; +} + +void shutdownSimADCs() +{ + assert(sim_global); + + sim_global_type::sims_t sims; + + { + /* swap out so the global lock is not held during delete */ + sim_global_type::guard_t G(sim_global->lock); + sims.swap(sim_global->sims); + } + + sims.clear(); +} + +SimADC::smart_pointer_type getSimADC(const std::string& name) +{ + epicsThreadOnce(&sim_mute_once, &sim_global_init, 0); + + sim_global_type::guard_t G(sim_global->lock); + + sim_global_type::sims_t::const_iterator it = sim_global->sims.find(name); + + if(it==sim_global->sims.end()) + return SimADC::smart_pointer_type(); + + return it->second; +} + diff --git a/testApp/remote/testServer.cpp b/testApp/remote/testServer.cpp index c6164b3..ca0f9df 100644 --- a/testApp/remote/testServer.cpp +++ b/testApp/remote/testServer.cpp @@ -15,12 +15,16 @@ #include +// TODO temp +#include "testADCSim.cpp" + using namespace epics::pvAccess; using namespace epics::pvData; using namespace std; using std::tr1::static_pointer_cast; + map structureStore; class StructureChangedCallback { @@ -134,6 +138,63 @@ public: } }; +// 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 { + + epicsGuard guard(adcSim->mutex); + + epicsUInt32 len = adcSim->prev_nSamples; + double *val = adcSim->data.value.get(); + static_pointer_cast(adcMatrix->getScalarArrayField("value", pvDouble))->put(0, len, val, 0); + + baseValue::shape_t* shape = &adcSim->data.shape; + size_t shapeLen = shape->size(); + vector intVal(shapeLen); + for (size_t i = 0; i < shapeLen; i++) + intVal[i] = (*shape)[i]; + static_pointer_cast(adcMatrix->getScalarArrayField("dim", pvInt))->put(0, shapeLen, &intVal[0], 0); + + 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; + } + + } + } + } +}; + + + + class ChannelFindRequesterImpl : public ChannelFindRequester { virtual void channelFindResult(epics::pvData::Status const & status, @@ -1161,6 +1222,7 @@ class MockChannel : public Channel { ChannelRequester::shared_pointer m_requester; String m_name; String m_remoteAddress; + public: // TODO PVStructure::shared_pointer m_pvStructure; protected: @@ -1273,6 +1335,42 @@ class MockChannel : public Channel { printf("=============------------------------------------!!!\n"); */ } + 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 + int dimValue = 0; + static_pointer_cast(m_pvStructure->getScalarArrayField("dim", pvInt))->put(0, 1, &dimValue, 0); + + 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; @@ -1510,14 +1608,18 @@ class MockServerChannelProvider : public ChannelProvider, MockServerChannelProvider() : m_mockChannelFind(), m_counterChannel(), + m_adcChannel(), m_scan1Hz(1.0), - m_scan1HzThread() + m_scan1HzThread(), + m_adcAction(), + m_adcThread() { } virtual ~MockServerChannelProvider() { m_scan1Hz.stopped.set(); + m_adcAction.stopped.set(); } void initialize() @@ -1534,6 +1636,12 @@ class MockServerChannelProvider : public ChannelProvider, m_scan1Hz.toProcess.push_back(process); m_scan1HzThread.reset(new Thread("process1hz", highPriority, &m_scan1Hz)); + + 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 Thread("adcThread", highPriority, &m_adcAction)); } virtual epics::pvData::String getProviderName() @@ -1576,6 +1684,11 @@ class MockServerChannelProvider : public ChannelProvider, channelRequester->channelCreated(Status::Ok, m_counterChannel); return m_counterChannel; } + else if (channelName == "testADC") + { + channelRequester->channelCreated(Status::Ok, m_adcChannel); + return m_adcChannel; + } else { ChannelProvider::shared_pointer chProviderPtr = shared_from_this(); @@ -1596,9 +1709,13 @@ class MockServerChannelProvider : public ChannelProvider, ChannelFind::shared_pointer m_mockChannelFind; Channel::shared_pointer m_counterChannel; + Channel::shared_pointer m_adcChannel; ProcessAction m_scan1Hz; auto_ptr m_scan1HzThread; + + ADCAction m_adcAction; + auto_ptr m_adcThread; }; @@ -1688,6 +1805,8 @@ int main(int argc, char *argv[]) testServer(timeToRun); + shutdownSimADCs(); + cout << "Done" << endl; if (cleanupAndReport)