Skip to content

Commit

Permalink
Avoid aborting initialization of output records.
Browse files Browse the repository at this point in the history
When a device is temporarily unvailable when the IOC is started, the
values of output records cannot be read from the device, and thus these
values cannot be initialized.

However, this should not keep the record initialization routine from
completing, so that the record can be processed at a later time.

Therefore, we now catch such exceptions and print an error message but
continue with initializing the record instead of destroying the device
support instance for the record.
  • Loading branch information
smarsching committed Sep 29, 2022
1 parent caaed74 commit 41d73ce
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 55 deletions.
66 changes: 43 additions & 23 deletions mrfApp/mrfEpicsSrc/MrfOutputRecord.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright 2015-2016 aquenos GmbH.
* Copyright 2015-2016 Karlsruhe Institute of Technology.
* Copyright 2015-2022 aquenos GmbH.
* Copyright 2015-2022 Karlsruhe Institute of Technology.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
Expand Down Expand Up @@ -34,6 +34,7 @@
#include <recGbl.h>

#include "MrfRecord.h"
#include "mrfEpicsError.h"

namespace anka {
namespace mrf {
Expand Down Expand Up @@ -113,27 +114,46 @@ MrfOutputRecord<RecordType>::MrfOutputRecord(RecordType *record) :
template<typename RecordType>
void MrfOutputRecord<RecordType>::initializeValue() {
if (this->getRecordAddress().isReadOnInit()) {
std::shared_ptr<MrfMemoryCache> deviceCache =
MrfDeviceRegistry::getInstance().getDeviceCache(
this->getRecordAddress().getDeviceId());
if (!deviceCache) {
throw std::runtime_error(
std::string("Could not find cache for device ")
+ this->getRecordAddress().getDeviceId() + ".");
}
switch (this->getRecordAddress().getDataType()) {
case MrfRecordAddress::DataType::uInt16:
this->writeRecordValue(
this->convertFromDevice(
deviceCache->readUInt16(
this->getRecordAddress().getMemoryAddress())));
break;
case MrfRecordAddress::DataType::uInt32:
this->writeRecordValue(
this->convertFromDevice(
deviceCache->readUInt32(
this->getRecordAddress().getMemoryAddress())));
break;
// We try to read the value from the device, so that we can initialize the
// record’s value. If this fails, we do not let the exception bubble up,
// because we still want the record to be initialized (so that it can be
// processed later). In this case, the record will simply stay in an
// undefined state (associated with an invalid alarm) until it is
// successfully processed for the first time.
try {
std::shared_ptr<MrfMemoryCache> deviceCache =
MrfDeviceRegistry::getInstance().getDeviceCache(
this->getRecordAddress().getDeviceId());
if (!deviceCache) {
throw std::runtime_error(
std::string("Could not find cache for device ")
+ this->getRecordAddress().getDeviceId() + ".");
}
switch (this->getRecordAddress().getDataType()) {
case MrfRecordAddress::DataType::uInt16:
this->writeRecordValue(
this->convertFromDevice(
deviceCache->readUInt16(
this->getRecordAddress().getMemoryAddress())));
break;
case MrfRecordAddress::DataType::uInt32:
this->writeRecordValue(
this->convertFromDevice(
deviceCache->readUInt32(
this->getRecordAddress().getMemoryAddress())));
break;
}
} catch (std::exception &e) {
errorExtendedPrintf(
"%s Reading initial value from device failed: %s",
this->getRecord()->name,
e.what());
return;
} catch (...) {
errorExtendedPrintf(
"%s Reading initial value from device failed: Unknown error.",
this->getRecord()->name);
return;
}
// The record's value has been initialized, therefore it is not undefined
// any longer.
Expand Down
100 changes: 68 additions & 32 deletions mrfApp/mrfEpicsSrc/MrfWaveformOutRecord.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright 2015-2016 aquenos GmbH.
* Copyright 2015-2016 Karlsruhe Institute of Technology.
* Copyright 2015-2022 aquenos GmbH.
* Copyright 2015-2022 Karlsruhe Institute of Technology.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
Expand All @@ -27,13 +27,15 @@
* of the GNU LGPL version 3 or newer.
*/

#include <algorithm>
#include <cstring>

#include <alarm.h>
#include <dbFldTypes.h>
#include <recGbl.h>

#include "MrfDeviceRegistry.h"
#include "mrfEpicsError.h"

#include "MrfWaveformOutRecord.h"

Expand Down Expand Up @@ -157,12 +159,35 @@ MrfWaveformOutRecord::MrfWaveformOutRecord(::waveformRecord *record) :
}
// Read current value from device and update record's value and last value
// written.
bool readFromDeviceSuccessful = true;
for (std::uint32_t arrayIndex = 0; arrayIndex < this->record->nelm;
++arrayIndex) {
std::uint32_t value = deviceCache->readUInt32(
this->address.getMemoryAddress()
+ (sizeof(std::uint32_t) + this->address.getElementDistance())
* arrayIndex);
// We try to read the value from the device, so that we can initialize the
// record’s value. If this fails, we do not let the exception bubble up,
// because we still want the record to be initialized (so that it can be
// processed later). In this case, the record will simply stay in an
// undefined state (associated with an invalid alarm) until it is
// successfully processed for the first time.
std::uint32_t value;
try {
value = deviceCache->readUInt32(
this->address.getMemoryAddress()
+ (sizeof(std::uint32_t) + this->address.getElementDistance())
* arrayIndex);
} catch (std::exception &e) {
errorExtendedPrintf(
"%s Reading initial value from device failed: %s",
this->record->name,
e.what());
readFromDeviceSuccessful = false;
break;
} catch (...) {
errorExtendedPrintf(
"%s Reading initial value from device failed: Unknown error.",
this->record->name);
readFromDeviceSuccessful = false;
break;
}
lastValueWritten[arrayIndex] = value;
lastValueWrittenValid[arrayIndex] = true;
switch (this->record->ftvl) {
Expand All @@ -180,34 +205,45 @@ MrfWaveformOutRecord::MrfWaveformOutRecord(::waveformRecord *record) :
break;
}
}
// The record's value has been initialized, thus it is not undefined any
// longer.
this->record->udf = false;
// We have to reset the alarm state explicitly, so that the record is not
// marked as invalid. This is not optimal because the record will not be
// placed in an alarm state if the value would usually trigger an alarm.
// However, alarms on output records are uncommon and we do not use them
// for the EVG or EVR, so this is fine. We also update the time stamp so
// that it represents the current time.
recGblGetTimeStamp(this->record);
recGblResetAlarms(this->record);
} else {
// Make sure that all elements are initialized with zeros.
switch (this->record->ftvl) {
case DBF_CHAR:
case DBF_UCHAR:
std::memset(this->record->bptr, 0, this->record->nelm);
break;
case DBF_SHORT:
case DBF_USHORT:
std::memset(this->record->bptr, 0, this->record->nelm * 2);
break;
case DBF_LONG:
case DBF_ULONG:
std::memset(this->record->bptr, 0, this->record->nelm * 4);
break;
if (readFromDeviceSuccessful) {
// The record's value has been initialized, thus it is not undefined any
// longer.
this->record->udf = false;
// We have to reset the alarm state explicitly, so that the record is not
// marked as invalid. This is not optimal because the record will not be
// placed in an alarm state if the value would usually trigger an alarm.
// However, alarms on output records are uncommon and we do not use them
// for the EVG or EVR, so this is fine. We also update the time stamp so
// that it represents the current time.
recGblGetTimeStamp(this->record);
recGblResetAlarms(this->record);
// We return because we do not want to initialize the record’s value with
// zeros.
return;
} else {
// We reset the last value-written valid flag, because we don’t want to
// use a partially initialized array. Having such an array could lead to
// very confusing behavior that would not be transparent to the user at
// all.
std::fill(
lastValueWrittenValid.begin(), lastValueWrittenValid.end(), false);
}
}
// Make sure that all elements are initialized with zeros.
switch (this->record->ftvl) {
case DBF_CHAR:
case DBF_UCHAR:
std::memset(this->record->bptr, 0, this->record->nelm);
break;
case DBF_SHORT:
case DBF_USHORT:
std::memset(this->record->bptr, 0, this->record->nelm * 2);
break;
case DBF_LONG:
case DBF_ULONG:
std::memset(this->record->bptr, 0, this->record->nelm * 4);
break;
}
}

void MrfWaveformOutRecord::processRecord() {
Expand Down

0 comments on commit 41d73ce

Please sign in to comment.