testADC added to testServer
This commit is contained in:
367
testApp/remote/testADCSim.cpp
Normal file
367
testApp/remote/testADCSim.cpp
Normal file
@@ -0,0 +1,367 @@
|
||||
#ifndef DBVALUE_H
|
||||
#define DBVALUE_H
|
||||
|
||||
#include <epicsTypes.h>
|
||||
#include <epicsTime.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <typeinfo>
|
||||
#include <tr1/memory>
|
||||
|
||||
struct baseValue {
|
||||
bool remoteWritable;
|
||||
|
||||
typedef std::vector<size_t> 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<typename PVT>
|
||||
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<std::string> choices;
|
||||
|
||||
scalarEnumValue() : value(0) {}
|
||||
};
|
||||
|
||||
struct scalarStringValue : public baseValue {
|
||||
typedef std::string value_type;
|
||||
typedef std::string element_type;
|
||||
|
||||
value_type value;
|
||||
|
||||
std::vector<std::string> choices;
|
||||
};
|
||||
|
||||
template<typename PVT>
|
||||
struct vectorNumericValue : public baseValue {
|
||||
typedef std::tr1::shared_ptr<PVT> value_type;
|
||||
typedef PVT element_type;
|
||||
|
||||
value_type value;
|
||||
|
||||
element_type displayHigh, displayLow;
|
||||
|
||||
vectorNumericValue() : displayHigh(0), displayLow(0) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename PVT>
|
||||
scalarNumericValue<PVT>::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 <tr1/functional>
|
||||
|
||||
#include <epicsThread.h>
|
||||
#include <epicsEvent.h>
|
||||
|
||||
struct SimADC : public std::tr1::enable_shared_from_this<SimADC>,
|
||||
public epicsThreadRunable
|
||||
{
|
||||
typedef std::tr1::shared_ptr<SimADC> smart_pointer_type;
|
||||
|
||||
epicsMutex mutex;
|
||||
|
||||
scalarNumericValue<double> mult, shift, offset, freq, rate;
|
||||
|
||||
scalarNumericValue<epicsUInt32> nSamples;
|
||||
epicsUInt32 prev_nSamples;
|
||||
|
||||
scalarEnumValue operation;
|
||||
|
||||
vectorNumericValue<double> data;
|
||||
|
||||
vectorNumericValue<epicsUInt32> 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 <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <epicsGuard.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
|
||||
// 180 / PI
|
||||
#define RAD2DEG 57.2957795131
|
||||
|
||||
// 100 / (2*PI)
|
||||
#define RAD2PCT 15.9154943092
|
||||
|
||||
struct sim_global_type {
|
||||
typedef epicsGuard<epicsMutex> guard_t;
|
||||
typedef epicsGuardRelease<epicsMutex> unguard_t;
|
||||
epicsMutex lock;
|
||||
typedef std::map<std::string, SimADC::smart_pointer_type> 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<typename T>
|
||||
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>());
|
||||
unsigned int *val = X.value.get();
|
||||
for(size_t i=0; i<prev_nSamples; i++)
|
||||
val[i] = 2*i;
|
||||
|
||||
X.shape.resize(1);
|
||||
X.shape[0] = prev_nSamples;
|
||||
|
||||
X.displayLow = val[0];
|
||||
X.displayHigh = val[prev_nSamples-1];
|
||||
}
|
||||
|
||||
if(!data.value || !data.value.unique())
|
||||
data.value.reset((double*)malloc(sizeof(double)*prev_nSamples), Freeme<double>());
|
||||
|
||||
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; i<prev_nSamples; i++)
|
||||
val[i] = mult.value * sin((freq.value/RAD2DEG)*i + (shift.value/RAD2PCT)) + offset.value;
|
||||
|
||||
data.shape.resize(1);
|
||||
data.shape[0] = prev_nSamples;
|
||||
|
||||
updated.signal();
|
||||
}
|
||||
|
||||
void SimADC::run()
|
||||
{
|
||||
const double min_sleep = epicsThreadSleepQuantum();
|
||||
|
||||
sim_global_type::guard_t G(mutex);
|
||||
|
||||
while(true) {
|
||||
{
|
||||
double zzz = rate.value>0.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;
|
||||
}
|
||||
|
||||
@@ -15,12 +15,16 @@
|
||||
|
||||
#include <pv/logger.h>
|
||||
|
||||
// TODO temp
|
||||
#include "testADCSim.cpp"
|
||||
|
||||
using namespace epics::pvAccess;
|
||||
using namespace epics::pvData;
|
||||
using namespace std;
|
||||
using std::tr1::static_pointer_cast;
|
||||
|
||||
|
||||
|
||||
map<String, PVStructure::shared_pointer> 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<epicsMutex> guard(adcSim->mutex);
|
||||
|
||||
epicsUInt32 len = adcSim->prev_nSamples;
|
||||
double *val = adcSim->data.value.get();
|
||||
static_pointer_cast<PVDoubleArray>(adcMatrix->getScalarArrayField("value", pvDouble))->put(0, len, val, 0);
|
||||
|
||||
baseValue::shape_t* shape = &adcSim->data.shape;
|
||||
size_t shapeLen = shape->size();
|
||||
vector<int> intVal(shapeLen);
|
||||
for (size_t i = 0; i < shapeLen; i++)
|
||||
intVal[i] = (*shape)[i];
|
||||
static_pointer_cast<PVIntArray>(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<PVIntArray>(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<MockChannel>(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<Thread> m_scan1HzThread;
|
||||
|
||||
ADCAction m_adcAction;
|
||||
auto_ptr<Thread> m_adcThread;
|
||||
};
|
||||
|
||||
|
||||
@@ -1688,6 +1805,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
testServer(timeToRun);
|
||||
|
||||
shutdownSimADCs();
|
||||
|
||||
cout << "Done" << endl;
|
||||
|
||||
if (cleanupAndReport)
|
||||
|
||||
Reference in New Issue
Block a user