Files
pvDatabase/src/database/pvRecord.cpp

482 lines
14 KiB
C++

/* pvRecord.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 2012.11.21
*/
#include <list>
#include <epicsGuard.h>
#include <epicsThread.h>
#include <pv/status.h>
#include <pv/pvAccess.h>
#include <pv/createRequest.h>
#include <pv/pvaVersion.h>
#include <pv/pvaVersionNum.h>
#include <pv/monitor.h>
#include <pv/convert.h>
#include <pv/rpcService.h>
#include <pv/timeStamp.h>
#include <pv/pvData.h>
#include <pv/rpcService.h>
#include <pv/pvTimeStamp.h>
#define epicsExportSharedSymbols
#include "pv/pvStructureCopy.h"
#include "pv/pvDatabase.h"
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvDatabase;
using namespace std;
namespace epics { namespace pvDatabase {
PVRecordPtr PVRecord::create(
string const &recordName,
PVStructurePtr const & pvStructure)
{
PVRecordPtr pvRecord(new PVRecord(recordName,pvStructure));
if(!pvRecord->init()) {
pvRecord.reset();
}
return pvRecord;
}
PVRecord::PVRecord(
string const & recordName,
PVStructurePtr const & pvStructure)
: recordName(recordName),
pvStructure(pvStructure),
depthGroupPut(0),
traceLevel(0),
isAddListener(false)
{
}
PVRecord::~PVRecord()
{
if(traceLevel>0) {
cout << "~PVRecord() " << recordName << endl;
}
}
void PVRecord::remove(bool callpvDatabaseRemoveRecord)
{
if(traceLevel>0) {
cout << "PVRecord::remove() " << recordName << endl;
}
epicsGuard<epics::pvData::Mutex> guard(mutex);
if(callpvDatabaseRemoveRecord) {
PVDatabasePtr pvDatabase(PVDatabase::getMaster());
if(pvDatabase) pvDatabase->removeRecord(shared_from_this(),false);
}
pvTimeStamp.detach();
for(std::list<PVListenerWPtr>::iterator iter = pvListenerList.begin();
iter!=pvListenerList.end();
iter++ )
{
PVListenerPtr listener = iter->lock();
if(!listener) continue;
if(traceLevel>0) {
cout << "PVRecord::remove() calling listener->unlisten " << recordName << endl;
}
listener->unlisten(shared_from_this());
}
pvListenerList.clear();
for (std::list<PVRecordClientWPtr>::iterator iter = clientList.begin();
iter!=clientList.end();
iter++ )
{
PVRecordClientPtr client = iter->lock();
if(!client) continue;
if(traceLevel>0) {
cout << "PVRecord::remove() calling client->detach " << recordName << endl;
}
client->detach(shared_from_this());
}
clientList.clear();
}
void PVRecord::initPVRecord()
{
PVRecordStructurePtr parent;
pvRecordStructure = PVRecordStructurePtr(
new PVRecordStructure(pvStructure,parent,shared_from_this()));
pvRecordStructure->init();
PVFieldPtr pvField = pvStructure->getSubField("timeStamp");
if(pvField) pvTimeStamp.attach(pvField);
}
void PVRecord::process()
{
if(traceLevel>2) {
cout << "PVRecord::process() " << recordName << endl;
}
if(pvTimeStamp.isAttached()) {
pvTimeStamp.get(timeStamp);
timeStamp.getCurrent();
pvTimeStamp.set(timeStamp);
}
}
PVRecordFieldPtr PVRecord::findPVRecordField(PVFieldPtr const & pvField)
{
return findPVRecordField(pvRecordStructure,pvField);
}
PVRecordFieldPtr PVRecord::findPVRecordField(
PVRecordStructurePtr const & pvrs,
PVFieldPtr const & pvField)
{
size_t desiredOffset = pvField->getFieldOffset();
PVFieldPtr pvf = pvrs->getPVField();
size_t offset = pvf->getFieldOffset();
if(offset==desiredOffset) return pvrs;
PVRecordFieldPtrArrayPtr pvrfpap = pvrs->getPVRecordFields();
PVRecordFieldPtrArray::iterator iter;
for (iter = pvrfpap.get()->begin(); iter!=pvrfpap.get()->end(); iter++ ) {
PVRecordFieldPtr pvrf = *iter;
pvf = pvrf->getPVField();
offset = pvf->getFieldOffset();
if(offset==desiredOffset) return pvrf;
size_t nextOffset = pvf->getNextFieldOffset();
if(nextOffset<=desiredOffset) continue;
return findPVRecordField(
static_pointer_cast<PVRecordStructure>(pvrf),
pvField);
}
throw std::logic_error(
recordName + " pvField "
+ pvField->getFieldName() + " not in PVRecord");
}
void PVRecord::lock() {
if(traceLevel>2) {
cout << "PVRecord::lock() " << recordName << endl;
}
mutex.lock();
}
void PVRecord::unlock() {
if(traceLevel>2) {
cout << "PVRecord::unlock() " << recordName << endl;
}
mutex.unlock();
}
bool PVRecord::tryLock() {
if(traceLevel>2) {
cout << "PVRecord::tryLock() " << recordName << endl;
}
return mutex.tryLock();
}
void PVRecord::lockOtherRecord(PVRecordPtr const & otherRecord)
{
if(traceLevel>2) {
cout << "PVRecord::lockOtherRecord() " << recordName << endl;
}
if(this<otherRecord.get()) {
otherRecord->lock();
return;
}
unlock();
otherRecord->lock();
lock();
}
bool PVRecord::addPVRecordClient(PVRecordClientPtr const & pvRecordClient)
{
if(traceLevel>1) {
cout << "PVRecord::addPVRecordClient() " << recordName << endl;
}
epicsGuard<epics::pvData::Mutex> guard(mutex);
// clean clientList
bool clientListClean = false;
while(!clientListClean) {
if(clientList.empty()) break;
clientListClean = true;
std::list<PVRecordClientWPtr>::iterator iter;
for (iter = clientList.begin();
iter!=clientList.end();
iter++ )
{
PVRecordClientPtr client = iter->lock();
if(client) continue;
if(traceLevel>1) {
cout << "PVRecord::addPVRecordClient() erasing client"
<< recordName << endl;
}
clientList.erase(iter);
clientListClean = false;
break;
}
}
clientList.push_back(pvRecordClient);
return true;
}
bool PVRecord::addListener(
PVListenerPtr const & pvListener,
epics::pvCopy::PVCopyPtr const & pvCopy)
{
if(traceLevel>1) {
cout << "PVRecord::addListener() " << recordName << endl;
}
epicsGuard<epics::pvData::Mutex> guard(mutex);
pvListenerList.push_back(pvListener);
this->pvListener = pvListener;
isAddListener = true;
pvCopy->traverseMaster(shared_from_this());
this->pvListener = PVListenerPtr();;
return true;
}
void PVRecord::nextMasterPVField(PVFieldPtr const & pvField)
{
PVRecordFieldPtr pvRecordField = findPVRecordField(pvField);
PVListenerPtr listener = pvListener.lock();
if(!listener.get()) return;
if(isAddListener) {
pvRecordField->addListener(listener);
} else {
pvRecordField->removeListener(listener);
}
}
bool PVRecord::removeListener(
PVListenerPtr const & pvListener,
epics::pvCopy::PVCopyPtr const & pvCopy)
{
if(traceLevel>1) {
cout << "PVRecord::removeListener() " << recordName << endl;
}
epicsGuard<epics::pvData::Mutex> guard(mutex);
std::list<PVListenerWPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ )
{
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
if(listener.get()==pvListener.get()) {
pvListenerList.erase(iter);
this->pvListener = pvListener;
isAddListener = false;
pvCopy->traverseMaster(shared_from_this());
this->pvListener = PVListenerPtr();
return true;
}
}
return false;
}
void PVRecord::beginGroupPut()
{
if(++depthGroupPut>1) return;
if(traceLevel>2) {
cout << "PVRecord::beginGroupPut() " << recordName << endl;
}
std::list<PVListenerWPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++)
{
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
listener->beginGroupPut(shared_from_this());
}
}
void PVRecord::endGroupPut()
{
if(--depthGroupPut>0) return;
if(traceLevel>2) {
cout << "PVRecord::endGroupPut() " << recordName << endl;
}
std::list<PVListenerWPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++)
{
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
listener->endGroupPut(shared_from_this());
}
}
std::ostream& operator<<(std::ostream& o, const PVRecord& record)
{
o << format::indent() << "record " << record.getRecordName() << endl;
{
format::indent_scope s(o);
o << *record.getPVRecordStructure()->getPVStructure();
}
return o;
}
PVRecordField::PVRecordField(
PVFieldPtr const & pvField,
PVRecordStructurePtr const &parent,
PVRecordPtr const & pvRecord)
: pvField(pvField),
isStructure(pvField->getField()->getType()==structure ? true : false),
parent(parent),
pvRecord(pvRecord)
{
}
void PVRecordField::init()
{
fullFieldName = pvField.lock()->getFieldName();
PVRecordStructurePtr pvParent(parent.lock());
while(pvParent) {
string parentName = pvParent->getPVField()->getFieldName();
if(parentName.size()>0) {
fullFieldName = pvParent->getPVField()->getFieldName()
+ '.' + fullFieldName;
}
pvParent = pvParent->getParent();
}
PVRecordPtr pvRecord(this->pvRecord.lock());
if(fullFieldName.size()>0) {
fullName = pvRecord->getRecordName() + '.' + fullFieldName;
} else {
fullName = pvRecord->getRecordName();
}
pvField.lock()->setPostHandler(shared_from_this());
}
PVRecordStructurePtr PVRecordField::getParent()
{
return parent.lock();
}
PVFieldPtr PVRecordField::getPVField() {return pvField.lock();}
string PVRecordField::getFullFieldName() {return fullFieldName; }
string PVRecordField::getFullName() {return fullName; }
PVRecordPtr PVRecordField::getPVRecord() {return pvRecord.lock();}
bool PVRecordField::addListener(PVListenerPtr const & pvListener)
{
PVRecordPtr pvRecord(this->pvRecord.lock());
if(pvRecord && pvRecord->getTraceLevel()>1) {
cout << "PVRecordField::addListener() " << getFullName() << endl;
}
pvListenerList.push_back(pvListener);
return true;
}
void PVRecordField::removeListener(PVListenerPtr const & pvListener)
{
PVRecordPtr pvRecord(this->pvRecord.lock());
if(pvRecord && pvRecord->getTraceLevel()>1) {
cout << "PVRecordField::removeListener() " << getFullName() << endl;
}
std::list<PVListenerWPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) {
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
if(listener.get()==pvListener.get()) {
pvListenerList.erase(iter);
return;
}
}
}
void PVRecordField::postPut()
{
PVRecordStructurePtr parent(this->parent.lock());;
if(parent) {
parent->postParent(shared_from_this());
}
postSubField();
}
void PVRecordField::postParent(PVRecordFieldPtr const & subField)
{
PVRecordStructurePtr pvrs = static_pointer_cast<PVRecordStructure>(shared_from_this());
std::list<PVListenerWPtr>::iterator iter;
for(iter = pvListenerList.begin(); iter != pvListenerList.end(); ++iter)
{
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
listener->dataPut(pvrs,subField);
}
PVRecordStructurePtr parent(this->parent.lock());
if(parent) parent->postParent(subField);
}
void PVRecordField::postSubField()
{
callListener();
if(isStructure) {
PVRecordStructurePtr pvrs =
static_pointer_cast<PVRecordStructure>(shared_from_this());
PVRecordFieldPtrArrayPtr pvRecordFields = pvrs->getPVRecordFields();
PVRecordFieldPtrArray::iterator iter;
for(iter = pvRecordFields->begin() ; iter !=pvRecordFields->end(); iter++) {
(*iter)->postSubField();
}
}
}
void PVRecordField::callListener()
{
std::list<PVListenerWPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) {
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
listener->dataPut(shared_from_this());
}
}
PVRecordStructure::PVRecordStructure(
PVStructurePtr const &pvStructure,
PVRecordStructurePtr const &parent,
PVRecordPtr const & pvRecord)
:
PVRecordField(pvStructure,parent,pvRecord),
pvStructure(pvStructure),
pvRecordFields(new PVRecordFieldPtrArray)
{
}
void PVRecordStructure::init()
{
PVRecordField::init();
const PVFieldPtrArray & pvFields = pvStructure.lock()->getPVFields();
size_t numFields = pvFields.size();
pvRecordFields->reserve( numFields);
PVRecordStructurePtr self =
static_pointer_cast<PVRecordStructure>(shared_from_this());
PVRecordPtr pvRecord = getPVRecord();
for(size_t i=0; i<numFields; i++) {
PVFieldPtr pvField = pvFields[i];
if(pvField->getField()->getType()==structure) {
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
PVRecordStructurePtr pvRecordStructure(
new PVRecordStructure(xxx,self,pvRecord));
pvRecordFields->push_back(pvRecordStructure);
pvRecordStructure->init();
} else {
PVRecordFieldPtr pvRecordField(
new PVRecordField(pvField,self,pvRecord));
pvRecordFields->push_back(pvRecordField);
pvRecordField->init();
}
}
}
PVRecordFieldPtrArrayPtr PVRecordStructure::getPVRecordFields()
{
return pvRecordFields;
}
PVStructurePtr PVRecordStructure::getPVStructure() {return pvStructure.lock();}
}}