diff --git a/src/Makefile b/src/Makefile index 1641005..46da9d8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -14,6 +14,7 @@ INC += pv/channelProviderLocal.h INC += pv/pvDatabase.h INC += pv/traceRecord.h INC += pv/removeRecord.h +INC += pv/processRecord.h INC += pv/pvSupport.h INC += pv/controlSupport.h INC += pv/scalarAlarmSupport.h diff --git a/src/pv/controlSupport.h b/src/pv/controlSupport.h index ca51107..be890ed 100644 --- a/src/pv/controlSupport.h +++ b/src/pv/controlSupport.h @@ -68,7 +68,7 @@ public: */ virtual void reset(); static ControlSupportPtr create(PVRecordPtr const & pvRecord); - static epics::pvData::StructureConstPtr controlField(); + static epics::pvData::StructureConstPtr controlField(epics::pvData::ScalarType scalarType); private: ControlSupport(PVRecordPtr const & pvRecord); PVRecordPtr pvRecord; @@ -77,8 +77,7 @@ private: epics::pvData::PVDoublePtr pvLimitLow; epics::pvData::PVDoublePtr pvLimitHigh; epics::pvData::PVDoublePtr pvMinStep; - epics::pvData::PVDoublePtr pvOutputValue; - double requestedValue; + epics::pvData::PVScalarPtr pvOutputValue; double currentValue; bool isMinStep; }; diff --git a/src/pv/processRecord.h b/src/pv/processRecord.h new file mode 100644 index 0000000..79b927c --- /dev/null +++ b/src/pv/processRecord.h @@ -0,0 +1,79 @@ +/* processRecord.h */ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvData is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/** + * @author mrk + * @date 2013.04.18 + */ +#ifndef PROCESSRECORD_H +#define PROCESSRECORD_H + +#include +#include +#include +#include +#include + +namespace epics { namespace pvDatabase { + +typedef std::tr1::shared_ptr EpicsThreadPtr; + +class ProcessRecord; +typedef std::tr1::shared_ptr ProcessRecordPtr; + +/** + * @brief Process another record in the same database. + * + * A record to process another record + * It is meant to be used via a channelPutGet request. + * The argument has one field: recordName. + * The result has a field named status. + */ +class epicsShareClass ProcessRecord : + public PVRecord, + public epicsThreadRunable +{ +public: + POINTER_DEFINITIONS(ProcessRecord); + /** + * Factory methods to create ProcessRecord. + * @param recordName The name for the ProcessRecord. + * @param delay Delay time to wait between process requests. + * @return A shared pointer to ProcessRecord. + */ + static ProcessRecordPtr create( + std::string const & recordName,double delay); + /** + * standard init method required by PVRecord + * @return true unless record name already exists. + */ + virtual bool init(); + /** + * @brief Process the record specified by recordName. + */ + virtual void process(); + virtual void run(); + void startThread(); + void stop(); +private: + ProcessRecord( + std::string const & recordName, + epics::pvData::PVStructurePtr const & pvStructure,double delay); + double delay; + EpicsThreadPtr thread; + epics::pvData::Event runStop; + epics::pvData::Event runReturn; + PVDatabasePtr pvDatabase; + PVRecordMap pvRecordMap; + epics::pvData::PVStringPtr pvCommand; + epics::pvData::PVStringPtr pvRecordName; + epics::pvData::PVStringPtr pvResult; + epics::pvData::Mutex mutex; +}; + +}} + +#endif /* PROCESSRECORD_H */ diff --git a/src/special/Makefile b/src/special/Makefile index 0b1f9a1..08400f1 100644 --- a/src/special/Makefile +++ b/src/special/Makefile @@ -4,9 +4,12 @@ SRC_DIRS += $(PVDATABASE_SRC)/special LIBSRCS += traceRecord.cpp LIBSRCS += removeRecord.cpp +LIBSRCS += processRecord.cpp DBD += traceRecordRegister.dbd DBD += removeRecordRegister.dbd +DBD += processRecordRegister.dbd LIBSRCS += traceRecordRegister.cpp LIBSRCS += removeRecordRegister.cpp +LIBSRCS += processRecordRegister.cpp diff --git a/src/special/processRecord.cpp b/src/special/processRecord.cpp new file mode 100644 index 0000000..6069121 --- /dev/null +++ b/src/special/processRecord.cpp @@ -0,0 +1,146 @@ +/* processRecord.cpp */ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvData is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/** + * @author mrk + * @date 2013.04.18 + */ +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include + +using std::tr1::static_pointer_cast; +using namespace epics::pvData; +using namespace epics::pvAccess; +using namespace std; + +namespace epics { namespace pvDatabase { + +ProcessRecordPtr ProcessRecord::create( + std::string const & recordName,double delay) +{ + FieldCreatePtr fieldCreate = getFieldCreate(); + PVDataCreatePtr pvDataCreate = getPVDataCreate(); + StructureConstPtr topStructure = fieldCreate->createFieldBuilder()-> + addNestedStructure("argument")-> + add("command",pvString)-> + add("recordName",pvString)-> + endNested()-> + addNestedStructure("result") -> + add("status",pvString) -> + endNested()-> + createStructure(); + PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure); + ProcessRecordPtr pvRecord( + new ProcessRecord(recordName,pvStructure,delay)); + if(!pvRecord->init()) pvRecord.reset(); + return pvRecord; +} + +void ProcessRecord::startThread() +{ + thread = EpicsThreadPtr(new epicsThread( + *this, + "processRecord", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityLow)); + thread->start(); +} + +void ProcessRecord::stop() +{ + runStop.signal(); + runReturn.wait(); +} + + +ProcessRecord::ProcessRecord( + std::string const & recordName, + epics::pvData::PVStructurePtr const & pvStructure,double delay) +: PVRecord(recordName,pvStructure), + delay(delay), + pvDatabase(PVDatabase::getMaster()) +{ +} + +bool ProcessRecord::init() +{ + initPVRecord(); + PVStructurePtr pvStructure = getPVStructure(); + pvCommand = pvStructure->getSubField("argument.command"); + pvRecordName = pvStructure->getSubField("argument.recordName"); + if(!pvRecordName) return false; + pvResult = pvStructure->getSubField("result.status"); + if(!pvResult) return false; + startThread(); + return true; +} + +void ProcessRecord::process() +{ + string recordName = pvRecordName->get(); + string command = pvCommand->get(); + if(command.compare("add")==0) { + epicsGuard guard(mutex); + std::map::iterator iter = pvRecordMap.find(recordName); + if(iter!=pvRecordMap.end()) { + pvResult->put(recordName + " already pesent"); + return; + } + PVRecordPtr pvRecord = pvDatabase->findRecord(recordName); + if(!pvRecord) { + pvResult->put(recordName + " not in pvDatabase"); + return; + } + pvRecordMap.insert(PVRecordMap::value_type(recordName,pvRecord)); + pvResult->put("success"); + return; + } else if(command.compare("remove")==0) { + epicsGuard guard(mutex); + std::map::iterator iter = pvRecordMap.find(recordName); + if(iter==pvRecordMap.end()) { + pvResult->put(recordName + " not found"); + return; + } + PVRecordPtr pvRecord = (*iter).second; + pvDatabase->removeRecord(pvRecord); + pvResult->put("success"); + return; + } else { + pvResult->put(command + " not a valid command: only add and remove are valid"); + return; + } +} + +void ProcessRecord::run() +{ + while(true) { + if(runStop.tryWait()) { + runReturn.signal(); + return; + } + if(delay>0.0) epicsThreadSleep(delay); + epicsGuard guard(mutex); + PVRecordMap::iterator iter; + for(iter = pvRecordMap.begin(); iter!=pvRecordMap.end(); ++iter) { + PVRecordPtr pvRecord = (*iter).second; + pvRecord->lock(); + pvRecord->beginGroupPut(); + pvRecord->process(); + pvRecord->endGroupPut(); + pvRecord->unlock(); + } + } +} + + +}} + diff --git a/src/special/processRecordRegister.cpp b/src/special/processRecordRegister.cpp new file mode 100644 index 0000000..6e129f4 --- /dev/null +++ b/src/special/processRecordRegister.cpp @@ -0,0 +1,72 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ + +/** + * @author mrk + * @date 2013.07.24 + */ + + +/* Author: Marty Kraimer */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +using namespace epics::pvData; +using namespace epics::pvAccess; +using namespace epics::pvDatabase; +using namespace std; + +static const iocshArg testArg0 = { "recordName", iocshArgString }; +static const iocshArg testArg1 = { "delay", iocshArgDouble }; +static const iocshArg *testArgs[] = { + &testArg0,&testArg1}; + +static const iocshFuncDef processRecordFuncDef = {"processRecordCreate", 2,testArgs}; + +static void processRecordCallFunc(const iocshArgBuf *args) +{ + char *recordName = args[0].sval; + if(!recordName) { + throw std::runtime_error("processRecordCreate invalid number of arguments"); + } + double delay = args[1].dval; + if(delay<0.0) delay = 1.0; + ProcessRecordPtr record = ProcessRecord::create(recordName,delay); + bool result = PVDatabase::getMaster()->addRecord(record); + if(!result) cout << "recordname" << " not added" << endl; +} + +static void processRecordRegister(void) +{ + static int firstTime = 1; + if (firstTime) { + firstTime = 0; + iocshRegister(&processRecordFuncDef, processRecordCallFunc); + } +} + +extern "C" { + epicsExportRegistrar(processRecordRegister); +} diff --git a/src/special/processRecordRegister.dbd b/src/special/processRecordRegister.dbd new file mode 100644 index 0000000..fd8f945 --- /dev/null +++ b/src/special/processRecordRegister.dbd @@ -0,0 +1 @@ +registrar("processRecordRegister") diff --git a/src/special/removeRecord.cpp b/src/special/removeRecord.cpp index 1e157de..7c234e0 100644 --- a/src/special/removeRecord.cpp +++ b/src/special/removeRecord.cpp @@ -8,8 +8,10 @@ * @author mrk * @date 2013.04.18 */ -#define epicsExportSharedSymbols +#include +#include +#define epicsExportSharedSymbols #include using std::tr1::static_pointer_cast; diff --git a/src/special/traceRecord.cpp b/src/special/traceRecord.cpp index 1c9ea66..9cc2d7f 100644 --- a/src/special/traceRecord.cpp +++ b/src/special/traceRecord.cpp @@ -8,8 +8,10 @@ * @author mrk * @date 2013.04.18 */ -#define epicsExportSharedSymbols +#include +#include +#define epicsExportSharedSymbols #include using std::tr1::static_pointer_cast; diff --git a/src/support/controlSupport.cpp b/src/support/controlSupport.cpp index 38cd7d6..f82bdcc 100644 --- a/src/support/controlSupport.cpp +++ b/src/support/controlSupport.cpp @@ -29,14 +29,14 @@ ControlSupport::~ControlSupport() cout << "ControlSupport::~ControlSupport()\n"; } -epics::pvData::StructureConstPtr ControlSupport::controlField() +epics::pvData::StructureConstPtr ControlSupport::controlField(ScalarType scalarType) { return FieldBuilder::begin() ->setId("control_t") ->add("limitLow", pvDouble) ->add("limitHigh", pvDouble) ->add("minStep", pvDouble) - ->add("outputValue", pvDouble) + ->add("outputValue", scalarType) ->createStructure(); } @@ -71,7 +71,7 @@ bool ControlSupport::init(PVFieldPtr const & pv,PVFieldPtr const & pvsup) pvLimitLow = pvControl->getSubField("limitLow"); pvLimitHigh = pvControl->getSubField("limitHigh"); pvMinStep = pvControl->getSubField("minStep"); - pvOutputValue = pvControl->getSubField("outputValue"); + pvOutputValue = pvControl->getSubField("outputValue"); } if(!pvControl || !pvLimitLow || !pvLimitHigh || !pvMinStep || !pvOutputValue) { cout << "ControlSupport for record " << pvRecord->getRecordName() @@ -79,8 +79,7 @@ bool ControlSupport::init(PVFieldPtr const & pv,PVFieldPtr const & pvsup) return false; } ConvertPtr convert = getConvert(); - requestedValue = convert->toDouble(pvValue); - currentValue = requestedValue; + currentValue = convert->toDouble(pvValue); isMinStep = false; return true; } @@ -89,38 +88,37 @@ bool ControlSupport::process() { ConvertPtr convert = getConvert(); double value = convert->toDouble(pvValue); - if(value==requestedValue&&value==currentValue) return false; - if(!isMinStep) requestedValue = value; + if(!isMinStep && value==currentValue) return false; double limitLow = pvLimitLow->get(); double limitHigh = pvLimitHigh->get(); double minStep = pvMinStep->get(); if(limitHigh>limitLow) { - if(requestedValue>limitHigh) requestedValue = limitHigh; - if(requestedValuelimitHigh) value = limitHigh; + if(value0.0) { if(diff<0.0) { outputValue = currentValue - minStep; isMinStep = true; - if(outputValuerequestedValue) { - outputValue = requestedValue; + if(outputValue>value) { + outputValue = value; isMinStep = false; } } } currentValue = outputValue; - pvOutputValue->put(outputValue); - if(!isMinStep && (outputValue!=requestedValue)) { - convert->fromDouble(pvValue,requestedValue); + convert->fromDouble(pvOutputValue,outputValue); + if(!isMinStep && (outputValue!=value)) { + convert->fromDouble(pvValue,value); } return true; }