// $Header: https://oberon.oapd.inaf.it/shins/trunk/shins/src/tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/ServiceWorker.cpp 3990 2022-07-06 17:08:30Z depa $
#include "tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/ServiceWorker.h"

#include "tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/SharknirBcuCcdDiagnosticRecord.h"
#include "tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/SharknirBcuSlopeDiagnosticRecord.h"
#include "tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/BcuException.h"
#include "tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/OverallDiagnosticRecord.h"
#include "tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/OverallDiagnosticRecordBuffer.h"
#include "tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/PropertyName.h"


#include "tiptilt_ctrl/bcu/util/Assertions.h"
#include "tiptilt_ctrl/bcu/util/Clock.h"

#include <Basda/Microgate/Mgp/MgpHeader.h>
#include <Basda/Microgate/Mgp/MemoryBuffer.h>
#include <Basda/Microgate/Driver/Exception.h>

#include <Nice/Property/BitSet.h>
#include <Nice/Qt/QtWidgetAttributeName.h>
#include <Nice/Util/Exception.h>
#include <Basda/Vino/Image.h>

#include <boost/assign/std/vector.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/static_assert.hpp>

#include <algorithm>
#include <cassert>
#include <string>


BOOST_STATIC_ASSERT(sizeof(Nice::Int) == sizeof(Basda::Microgate::DWord));


using Sharknir::util::ClockPtr;
using boost::lexical_cast;
using boost::make_shared;
using std::string;
using namespace Basda::Microgate;

#undef LOG_NAME
#define LOG_NAME m_name


#define assertThreadSafeDiagnosticsBuffer(diagnosticRingBuffer) {}

namespace Sharknir {
    namespace Bcu {

      enum ShinsOpCode
        {
         MGP_OP_WR_DMXINETICS_RAM = 139,
        };

#define SHARKNIR_BCU_CONVERT_STD_EXCEPTION_FOR_BASDA_CLIENT_BEGIN() \
    try {

#define SHARKNIR_BCU_CONVERT_STD_EXCEPTION_FOR_BASDA_CLIENT_END()  \
    }                                                           \
    catch (std::exception& e)                                   \
    {                                                           \
        E_LOG(e.what());                                        \
        E_THROW(Sharknir::BcuServiceException, e.what());          \
    }

#define SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_ENTRY(OP_DESCRIPTION) \
    Nice::Guard lock(deviceMutex_);                                      \
    try {

#define SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_EXIT(OP_DESCRIPTION)       \
    }                                                                         \
    catch (std::exception& e)                                                 \
    {                                                                         \
        E_LOG("Failed to perform operation '" << OP_DESCRIPTION <<            \
                "' with reason '" << e.what() << "'");                        \
        setDeviceAsUnreachable();                                             \
        E_THROW(Sharknir::BcuServiceException, e.what());                        \
    }

#define SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_WRITE_ENTRY_LOG(                \
            memoryType, firstDsp, lastDsp, address, buffer)               \
    D_LOG(name() << " got request for writing to " << memoryType <<       \
            " memory at address 0x" <<  std::hex << address <<  std::dec << \
            " to the DSP range [" << firstDsp << ":" << lastDsp           \
            << "] " << buffer.size() << " DWords");

#define SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_READ_ENTRY_LOG(                \
           memoryType, dspIndex, address, lengthInDword)                 \
       D_LOG(name() << " got request for reading from " << memoryType << \
            " memory at address 0x" << std::hex << address << std::dec <<  \
            " "  <<  lengthInDword << " DWords");


int ServiceWorker::DeviceAsyncImpl::asyncWait(const Nice::Time & _t)
{
      return m_worker->timedWaitForNewState(_t);
}

int ServiceWorker::DeviceAsyncImpl::asyncStatus(const std::string & _s, int _d)
{
   m_worker->cmdAction(_d, _s);
   return 0;
}

ServiceWorker::ServiceWorker(const Nice::JointPtr & conf,
                             const OverallDiagnosticRecordBufferPtr& diagnosticBuffer,
                             const ClockPtr& clock)
    :ServiceWorker::BcuServiceWorkerSkeleton(conf),
     diagnosticsBuffer_(diagnosticBuffer),
     clock_(clock),
     diagnosticsSnapshotTimeout_(Nice::Time::seconds(30)),
     nMaxConsecutiveCommunicationGlitches_(0),
     communicationGlitchCount_(0)
{
    SHARKNIR_PRECONDITION(0 != conf, "Conf is valid");

    m_deviceAsync.m_worker = this;

    config()->setAtNode<string>(PropertyName::name(), this->name());
    if (! config()->exist(PropertyName::pollingIntervalInMillisecond()))
    {
        config()->setAtNode<int>(PropertyName::pollingIntervalInMillisecond(),
            1000);
    }

    initializeCounterProperties();
    setDeviceAsUnreachable();
}

ServiceWorker::~ServiceWorker()
{
}

/**
 * @brief      Initialize ServiceWorker
 */
void ServiceWorker::init()
{
    assert(0 != activeDispatcher_);
    SHARKNIR_ENSURE(! activeDispatcher_->isAlive(), "BCU diagnostics dispatcher is inactive.");
    activeDispatcher_->start();
    N_LOG(STARTED_UP_MESSAGE());
}


/**
 * @brief      { function_description }
 */
void ServiceWorker::deinit()
{
    D_LOG("Deinitalizing");
    SHARKNIR_ENSURE(activeDispatcher_->isAlive(), "BCU diagnostics dispatcher is active.");
    activeDispatcher_->terminate();
}

void ServiceWorker::activate()
{
    D_LOG("Activating");
}

void ServiceWorker::deactivate()
{
    D_LOG("Deactivating");
}

void ServiceWorker::terminate()
{
  N_LOG("Terminating")
}


void ServiceWorker::stop()
{
  changeStateOnlyFromTo(Basda::WORKING, Basda::ONLINE);
}

void ServiceWorker::abort()
{
  N_LOG("Aborting");
}

void ServiceWorker::idleOnline()
{
    D_LOG("Idling online");

    int period = config()->Nice::Joint::getWithDefault<int>(PropertyName::pollingIntervalInMillisecond(), 1000);
    if(isStateTransitionRequested(period))
    {
        return;
    }

    keepHouse();
}

void ServiceWorker::resetDevices(int firstDsp, int lastDsp, int address, const Nice::SeqInt & buffer)
{
    SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_WRITE_ENTRY_LOG("ResetDevices", firstDsp, lastDsp, address, buffer);

    writeToBcu(Basda::Microgate::MGP_OP_RESET_DEVICES, firstDsp, lastDsp, address, buffer, "resetDevices", PropertyName::numOfExternallyWrittenResetDevicesDWords());
}


void ServiceWorker::writeDMIF(int firstDsp, int lastDsp, int address, const Nice::SeqInt& buffer)
{
  SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_WRITE_ENTRY_LOG("DMIF", firstDsp, lastDsp, address, buffer);


  writeToBcu(Basda::Microgate::MGP_OP_WR_DMXINETICS_RAM,
             firstDsp, lastDsp,
             address, buffer, "initDMIF",
             PropertyName::numOfDMInterfaceAccess());
}


void ServiceWorker::writeShMRam(int firstDsp, int lastDsp, int address, const Nice::SeqInt& buffer)
{
    SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_WRITE_ENTRY_LOG("SHMRAM", firstDsp, lastDsp, address, buffer);
    U5_LOG("calling writeToBcu with MGP command " << Basda::Microgate::MGP_OP_WR_SCIMEASURE_RAM);
    writeToBcu(Basda::Microgate::MGP_OP_WR_SCIMEASURE_RAM, firstDsp, lastDsp, address, buffer, "writeShMRam", PropertyName::numOfExternallyWrittenShmramDWords());
}


void ServiceWorker::writeDsp(int firstDsp, int lastDsp, int address, const Nice::SeqInt& buffer)
{
    SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_WRITE_ENTRY_LOG("DSP", firstDsp, lastDsp, address, buffer)

    writeToBcu(Basda::Microgate::MGP_OP_WRSAME_DSP, firstDsp, lastDsp, address, buffer, "writeDsp", PropertyName::numOfExternallyWrittenDspDWords());
}

void ServiceWorker::writeSdram(int firstDsp, int lastDsp, int address, const Nice::SeqInt & buffer)
{
    SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_WRITE_ENTRY_LOG("SDRAM", firstDsp, lastDsp, address, buffer);

    writeToBcu(Basda::Microgate::MGP_OP_WRSAME_SDRAM, firstDsp, lastDsp, address, buffer, "writeSdram",
            PropertyName::numOfExternallyWrittenSdramDWords());
}

void ServiceWorker::writeSram(int firstDsp, int lastDsp, int address, const Nice::SeqInt & buffer)
{
    SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_WRITE_ENTRY_LOG("SRAM", firstDsp, lastDsp, address, buffer)

    writeToBcu(Basda::Microgate::MGP_OP_WRSAME_SRAM, firstDsp, lastDsp, address, buffer, "writeSram", PropertyName::numOfExternallyWrittenSramDWords());
}

Nice::SeqInt ServiceWorker::readDsp(int dspIndex, int address, int lengthInDword)
{
    SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_READ_ENTRY_LOG("DSP", dspIndex, address, lengthInDword)

    return readFromBcu(Basda::Microgate::MGP_OP_RDSEQ_DSP, dspIndex, address, lengthInDword, "readDsp", PropertyName::numOfExternallyReadDspDWords());
}

Nice::SeqInt ServiceWorker::readSdram(int dspIndex, int address, int lengthInDword)
{
    SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_READ_ENTRY_LOG("SDRAM", dspIndex, address, lengthInDword);

    return readFromBcu(Basda::Microgate::MGP_OP_RDSEQ_SDRAM, dspIndex, address, lengthInDword, "readSdram", PropertyName::numOfExternallyReadSdramDWords());
}

Nice::SeqInt ServiceWorker::readDiags(int dspIndex, int address, int lengthInDword)
{
    W_LOG("dspIndex : " << dspIndex << "; address : " << address << "; lengthInDword : " << lengthInDword);

    SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_READ_ENTRY_LOG("SDRAM", dspIndex, address, lengthInDword);

    Basda::Microgate::OpCode opcode = Basda::Microgate::MGP_OP_RDSEQ_SDRAM;
    const uint8_t validDspIndex= boost::numeric_cast<uint8_t>(dspIndex);
    static Nice::SeqInt result;

    result.resize(lengthInDword);
    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_ENTRY("readDiags")

    DevicePtr _d = getDevice();
    int read_data = 0;
    int block = 45*1024;//Empiric on vultus
    if(block > lengthInDword)
    {
        block = lengthInDword;
    }
//    block = lengthInDword;
    while(read_data < lengthInDword)
    {
        Basda::Microgate::MemoryBufferPtr b = _d->read(opcode, validDspIndex, validDspIndex, address + read_data, block);
        std::memcpy(&*result.begin() + read_data, b->getBeginOfBuffer(), sizeof(int)*block);
        read_data += block;
        if(read_data + block > lengthInDword)
        {
            block = lengthInDword - read_data;
        }
    }

    int already_read = config()->getAtNode<int>(PropertyName::numOfExternallyReadSdramDWords());
    config()->setAtNode<int>(PropertyName::numOfExternallyReadSdramDWords(), already_read + read_data);

    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_EXIT("readDiags")

    return result;
}




Nice::SeqInt ServiceWorker::readSram(int dspIndex, int address, int lengthInDword)
{
    SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_READ_ENTRY_LOG("SRAM", dspIndex, address, lengthInDword)

    return readFromBcu(Basda::Microgate::MGP_OP_RDSEQ_SRAM, dspIndex, address, lengthInDword, "readSram", PropertyName::numOfExternallyReadSramDWords());
}


Nice::SeqInt ServiceWorker::readShMRam(int dspIndex, int address, int lengthInDword)
{
  SHARKNIR_BCU_SERVICE_WORKER_EXTERNAL_READ_ENTRY_LOG("SHMRAM", dspIndex, address, lengthInDword);

  return readFromBcu(Basda::Microgate::MGP_OP_RD_SCIMEASURE_RAM, dspIndex, address, lengthInDword, "readShMRam", PropertyName::numOfExternallyReadShmramDWords());
}


void ServiceWorker::uploadDspCode(int firstDsp, int lastDsp, const std::string& dspProgramPathOnServer)
{
    N_LOG("Uploading DSP code " << dspProgramPathOnServer << " to DSP range [" << firstDsp << ":" << lastDsp << "]")

    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_ENTRY("uploadDspCode")

    Nice::File dspProgram(dspProgramPathOnServer);
    const bool forceLoading= true;
    dspProgram.resolve(forceLoading);
    getDevice()->uploadDspCode(boost::numeric_cast<uint8_t>(firstDsp), boost::numeric_cast<uint8_t>(lastDsp), dspProgram);

    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_EXIT("uploadDspCode")
}

void ServiceWorker::enableMasterDiagnostics(Nice::U16 decimationFactor,
              const std::string & remoteHostIpv4Address,
              const std::string & remoteHostMacAddress,
              Nice::U16 remoteHostUdpPort)
{
    N_LOG("Enabling master diagnostics with decimation factor " <<
            decimationFactor <<
            " remote host IPv4 address: " << remoteHostIpv4Address <<
            " remote host MAC address: " << remoteHostMacAddress <<
            " remote host UDP port: " << remoteHostUdpPort);

    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_ENTRY("enableMasterDiagnostics")

    getDevice()->enableMasterDiagnostics(decimationFactor, remoteHostIpv4Address, remoteHostMacAddress, remoteHostUdpPort);

    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_EXIT("enableMasterDiagnostics")
}

#define SHARKNIR_BCU_RETURN_PAST_RECORDS_BY_TYPE(type, nMaxRecords) \
{                                                                \
    checkNumOfRequestedOverallDiagnosticRecords(nMaxRecords);    \
    checkDiagnosticRecordAvailability((nMaxRecords));            \
                                                                 \
    SHARKNIR_BCU_CONVERT_STD_EXCEPTION_FOR_BASDA_CLIENT_BEGIN()     \
    assertThreadSafeDiagnosticsBuffer(diagnosticsBuffer_);       \
                                                                 \
    return *(diagnosticsBuffer_->                                \
                    getPast ## type ## OverallDiagnosticRecords( \
                                                (nMaxRecords))); \
    SHARKNIR_BCU_CONVERT_STD_EXCEPTION_FOR_BASDA_CLIENT_END()       \
                                                                 \
    assert(false);                                               \
    return SeqOverallDiagnosticRecord();                         \
}

SeqOverallDiagnosticRecord ServiceWorker::getPastOverallDiagnosticRecordsInOnePass(int nRecords)
{
    D_LOG("Getting " << nRecords << " past overall diagnostic records")
    SHARKNIR_BCU_RETURN_PAST_RECORDS_BY_TYPE(, nRecords)
}

#define SHARKNIR_BCU_RETURN_FUTURE_RECORDS_BY_TYPE(type, nFutureRecords, \
                                                timeout)              \
{                                                                     \
    checkNumOfRequestedOverallDiagnosticRecords(nFutureRecords);      \
                                                                      \
    SHARKNIR_BCU_CONVERT_STD_EXCEPTION_FOR_BASDA_CLIENT_BEGIN()          \
    assertThreadSafeDiagnosticsBuffer(diagnosticsBuffer_)             \
    return *(diagnosticsBuffer_->                                     \
                    getFuture ## type ## OverallDiagnosticRecords(    \
            nFutureRecords, timeout));                                \
    SHARKNIR_BCU_CONVERT_STD_EXCEPTION_FOR_BASDA_CLIENT_END()            \
                                                                      \
    assert(false);                                                    \
    return SeqOverallDiagnosticRecord();                              \
}

SeqOverallDiagnosticRecord ServiceWorker::getFutureOverallDiagnosticRecordsInOnePass(int nFutureRecords, Nice::Time timeout)
{
    D_LOG("Getting " << nFutureRecords << " future overall records with timeout of " << timeout)
    SHARKNIR_BCU_RETURN_FUTURE_RECORDS_BY_TYPE(, nFutureRecords, timeout)
}

SeqOverallDiagnosticRecord ServiceWorker::getPastCcdOverallDiagnosticRecordsInOnePass(int nMaxRecords)
{
    D_LOG("Getting " << nMaxRecords << " CCD overall diagnostic records")
    SHARKNIR_BCU_RETURN_PAST_RECORDS_BY_TYPE(Ccd, nMaxRecords)
}

SeqOverallDiagnosticRecord ServiceWorker::getPastSlopeOverallDiagnosticRecordsInOnePass(int nMaxRecords)
{
    D_LOG("Getting " << nMaxRecords << " slope overall diagnostic records")
    SHARKNIR_BCU_RETURN_PAST_RECORDS_BY_TYPE(Slope, nMaxRecords)
}

SeqOverallDiagnosticRecord ServiceWorker::getFutureCcdOverallDiagnosticRecordsInOnePass(int nMaxRecords, Nice::Time timeout)
{
    D_LOG("Getting " << nMaxRecords << " future CCD overall diagnostic records" << " with timeout of " << timeout )
    SHARKNIR_BCU_RETURN_FUTURE_RECORDS_BY_TYPE(Ccd, nMaxRecords, timeout)
}

SeqOverallDiagnosticRecord ServiceWorker::getFutureSlopeOverallDiagnosticRecordsInOnePass(int nMaxRecords, Nice::Time timeout)
{
    D_LOG("Getting " << nMaxRecords << " future slope overall diagnostic records" << " with timeout of " << timeout)
    SHARKNIR_BCU_RETURN_FUTURE_RECORDS_BY_TYPE(Slope, nMaxRecords, timeout)
}

#define SHARKNIR_BCU_createOverallDiagnosticRecordSnapshotWith(     \
    method, nRecords, cookie, callerName)                        \
    do                                                           \
    {                                                            \
    assertThreadSafeDiagnosticsBuffer(diagnosticsBuffer_);       \
    SeqOverallDiagnosticRecordPtr records= diagnosticsBuffer_->  \
                            method(nRecords);                    \
                                                                 \
    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_ENTRY(callerName) \
    addRecordsToSnapshot(cookie, records);                       \
    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_EXIT(callerName)  \
    } while (0)


void ServiceWorker::createPastOverallDiagnosticRecordSnapshot(const std::string& cookie, int nRecords)
{
    checkNumOfRequestedOverallDiagnosticRecords(nRecords);
    checkDiagnosticRecordAvailability(nRecords);

    D_LOG("Creating past overall diagnostic record snapshot " << cookie << " for " << nRecords << " records.")

    SHARKNIR_BCU_createOverallDiagnosticRecordSnapshotWith(getPastOverallDiagnosticRecords, nRecords, cookie, "createPastOverallDiagnosticRecordSnapshot");
}

void ServiceWorker::freePastOverallDiagnosticRecordSnapshot(const std::string& cookie)
{
    D_LOG("Freeing past overall diagnostic record snapshot " << cookie << ".")

    freeOverallDiagnosticRecordSnapshot(cookie);
}

SeqOverallDiagnosticRecord ServiceWorker::getPastOverallDiagnosticRecordsFromSnapshot(const std::string& cookie, int startRecordIndex, int nRecords)
{
    D_LOG("Getting " << nRecords <<
            " past overall diagnostic records from snapshot " << cookie << " starting at index " << startRecordIndex)

    return getOverallDiagnosticRecordsFromSnapshot(cookie, startRecordIndex, nRecords);
}

void ServiceWorker::createPastSlopeOverallDiagnosticRecordSnapshot(const string& cookie, int nRecords)
{
	checkNumOfRequestedOverallDiagnosticRecords(nRecords);
    checkDiagnosticRecordAvailability(nRecords);

    D_LOG("Creating past slope diagnostic record snapshot " << cookie << " for " << nRecords << " records.")

    SHARKNIR_BCU_createOverallDiagnosticRecordSnapshotWith(getPastSlopeOverallDiagnosticRecords, nRecords, cookie, "createPastSlopeOverallDiagnosticRecordSnapshot");
}

void ServiceWorker::freePastSlopeOverallDiagnosticRecordSnapshot(const std::string& cookie)
{
	D_LOG("Freeing past slope overall diagnostic record snapshot " << cookie << ".")
    freeOverallDiagnosticRecordSnapshot(cookie);
}

SeqOverallDiagnosticRecord ServiceWorker::getPastSlopeOverallDiagnosticRecordsFromSnapshot(const std::string& cookie, int startRecordIndex, int nRecords)
{
	D_LOG("Getting " << nRecords <<
		  " past slope overall diagnostic records from snapshot " << cookie << " starting at index " << nRecords);

	return getOverallDiagnosticRecordsFromSnapshot(cookie, startRecordIndex, nRecords);
}

#define SHARKNIR_BCU_createFutureOverallDiagnosticRecordWith(     \
	retrieveMethod, callerName)                                \
	do                                                         \
	{                                                          \
        checkNumOfRequestedOverallDiagnosticRecords(nRecords); \
        assertThreadSafeDiagnosticsBuffer(diagnosticsBuffer_)  \
        SeqOverallDiagnosticRecordPtr records=                 \
	        diagnosticsBuffer_-> retrieveMethod (nRecords,     \
	                timeout);                                  \
                                                               \
        SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_ENTRY(      \
            callerName)                                        \
        addRecordsToSnapshot(cookie, records);                 \
        SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_EXIT(       \
            callerName)                                        \
    } while (0)


void ServiceWorker::createFutureOverallDiagnosticRecordSnapshot(const std::string& cookie, int nRecords, Nice::Time timeout)
{
    D_LOG("Creating future overall diagnostic record snapshot " << cookie << " for " << nRecords << " records and timeout " << timeout)

    SHARKNIR_BCU_createFutureOverallDiagnosticRecordWith(getFutureOverallDiagnosticRecords, "createFutureOverallDiagnosticRecordSnapshot");
}

void ServiceWorker::freeFutureOverallDiagnosticRecordSnapshot(const std::string& cookie)
{
	D_LOG("Freeing future overall diagnostic record snapshot " << cookie);
    freeOverallDiagnosticRecordSnapshot(cookie);
}

SeqOverallDiagnosticRecord ServiceWorker::getFutureOverallDiagnosticRecordsFromSnapshot(const std::string & cookie, int startRecordIndex, int nRecords)
{
    D_LOG("Getting " << nRecords << " future overall diagnostic records from snapshot " << cookie << " starting from record index " << startRecordIndex)

    return getOverallDiagnosticRecordsFromSnapshot(cookie, startRecordIndex, nRecords);
}


void ServiceWorker::createFutureSlopeOverallDiagnosticRecordSnapshot(const std::string& cookie, int nRecords, Nice::Time timeout)
{
	D_LOG("Creating future slope overall diagnostic record snapshot " << cookie << "for " << nRecords << " records and timeout " << timeout);

	SHARKNIR_BCU_createFutureOverallDiagnosticRecordWith(getFutureSlopeOverallDiagnosticRecords, "createFutureSlopeOverallDiagnosticRecordSnapshot");
}

SeqOverallDiagnosticRecord ServiceWorker::getFutureSlopeOverallDiagnosticRecordsFromSnapshot(const std::string& cookie, int startRecordIndex, int nRecords)
{
	D_LOG("Getting " << nRecords << " future slope overall diagnostic records from snapshot " << cookie << "starting from record index " << startRecordIndex);
	return getOverallDiagnosticRecordsFromSnapshot(cookie, startRecordIndex, nRecords);
}

void ServiceWorker::freeFutureSlopeOverallDiagnosticRecordSnapshot(const std::string& cookie)
{
	D_LOG("Freeing future slope overall diagnostic record snapshot " << cookie);
	freeOverallDiagnosticRecordSnapshot(cookie);
}

void ServiceWorker::setDeviceFactory(const Sharknir::Bcu::DeviceFactoryPtr& f)
{
    SHARKNIR_PRECONDITION(0 != f, "factory is valid");
    deviceFactory_= f;
}

void ServiceWorker::setActiveDiagnosticsCollector(const ActiveDiagnosticsCollectorPtr& collector)
{
    SHARKNIR_PRECONDITION(0 != collector, "Collector is valid");
    activeDispatcher_= collector;
}

void ServiceWorker::setMaxNumOfTolerableCommunicationGlitches(unsigned nReplyFailures)
{
    N_LOG("Setting max number of tolerable consecutive"
            " communication glitches to " << nReplyFailures << ".")
    nMaxConsecutiveCommunicationGlitches_= nReplyFailures;
}

bool ServiceWorker::isStateTransitionRequested(int timeoutInMilliseconds)
{
    return Basda::ServiceWorker::timedWaitForNewState(
            Nice::Time::milliSeconds(timeoutInMilliseconds)) != 0;
}

void ServiceWorker::updateProperties()
{
    double tempInC;
    tempInC = getDevice()->readBcuFpgaTemperatureInC();
    updateTemperatureProperty(PropertyName::bcuFpgaTemperatureInC(), tempInC);

    tempInC= getDevice()->readBcuPcbTemperatureInC();
    updateTemperatureProperty(PropertyName::bcuPcbTemperatureInC(), tempInC);

    // tempInC= getDevice()->readDsp16do0FpgaTemperatureInC();
    // updateTemperatureProperty(PropertyName::dsp16do0FpgaTemperatureInC(), tempInC);

    // tempInC= getDevice()->readDsp16do0PcbTemperatureInC();
    // updateTemperatureProperty(PropertyName::dsp16do0PcbTemperatureInC(), tempInC);

    // tempInC= getDevice()->readDsp16do1FpgaTemperatureInC();
    // updateTemperatureProperty(PropertyName::dsp16do1FpgaTemperatureInC(), tempInC);

    // tempInC= getDevice()->readDsp16do1PcbTemperatureInC();
    // updateTemperatureProperty(PropertyName::dsp16do1PcbTemperatureInC(), tempInC);

    // tempInC= getDevice()->readHvc0FpgaTemperatureInC();
    // updateTemperatureProperty(PropertyName::hvc0FpgaTemperatureInC(), tempInC);

    // tempInC= getDevice()->readHvc0PcbTemperatureInC();
    // updateTemperatureProperty(PropertyName::hvc0PcbTemperatureInC(), tempInC);

    // tempInC= getDevice()->readHvc1FpgaTemperatureInC();
    // updateTemperatureProperty(PropertyName::hvc1FpgaTemperatureInC(), tempInC);

    // tempInC= getDevice()->readHvc1PcbTemperatureInC();
    // updateTemperatureProperty(PropertyName::hvc1PcbTemperatureInC(), tempInC);

    updateDiagnosticsProperties();
}


void ServiceWorker::warnThatTemperatureExceedsLimit(const std::string& propertyName, double temperatureInC)
{
    W_LOG("Temperature for property " << propertyName << " has too high value (" << temperatureInC << " degree Celsius)");
}


void ServiceWorker::setDiagnosticsSnapshotTimeout(const Nice::Time& timeout)
{
    diagnosticsSnapshotTimeout_= timeout;
}


void ServiceWorker::initializeCounterProperties()
{
    config()->setAtNode<int>(PropertyName::numOfExternallyReadDspDWords(), 0);
    config()->setAtNode<int>(
            PropertyName::numOfExternallyReadSramDWords(), 0);
    config()->setAtNode<int>(
            PropertyName::numOfExternallyReadShmramDWords(), 0);
    config()->setAtNode<int>(
            PropertyName::numOfExternallyReadSdramDWords(), 0);
    config()->setAtNode<int>(
            PropertyName::numOfExternallyWrittenDspDWords(), 0);
    config()->setAtNode<int>(
            PropertyName::numOfExternallyWrittenResetDevicesDWords(), 0);
    config()->setAtNode<int>(
            PropertyName::numOfExternallyWrittenSdramDWords(), 0);
    config()->setAtNode<int>(
            PropertyName::numOfExternallyWrittenSramDWords(), 0);
    config()->setAtNode<int>(
            PropertyName::numOfExternallyWrittenShmramDWords(), 0);
    config()->setAtNode<int>(PropertyName::slopeRecordReceptionCounter(), 0);
    config()->setAtNode<int>(PropertyName::ccdRecordReceptionCounter(), 0);
    // config()->setAtNode<int>(PropertyName::asmRecordReceptionCounter(), 0);
    config()->setAtNode<int>(
            PropertyName::slopeRecordDuplicateReceptionCounter(), 0);
    config()->setAtNode<int>(
            PropertyName::ccdRecordDuplicateReceptionCounter(), 0);
    // config()->setAtNode<int>(
    //         PropertyName::asmRecordDuplicateReceptionCounter(), 0);
    config()->setAtNode<int>(PropertyName::highestReceivedFrameCounter(), 0);
    config()->setAtNode<int>(PropertyName::lateAsmRecordCounter(), 0);
    config()->setAtNode<int>(PropertyName::lateCcdRecordCounter(), 0);
    config()->setAtNode<int>(PropertyName::lateSlopeRecordCounter(), 0);
    addDummyCcdImageProperty();
}


void ServiceWorker::updateDiagnosticsProperties()
{
    updateDiagnosticsCounterProperties();
    updateSlopeDiagnostics();
    updateLastestPnCCDImage();
}


void ServiceWorker::updateSlopeRecordParametersFrom(const SharknirBcuSlopeDiagnosticRecord* slopeRecord)
{
    const FrameCounter fc= slopeRecord->sc_HeaderDiag.dsc_FramesCounter;
    config()->setAtNode<string>(
            PropertyName::currentDiagnosticsSlopeFrameCounter(),
            (boost::format("%d") % fc).str());
    const uint32_t paramSelector= slopeRecord->sc_HeaderDiag.dsc_ParamSelector;
    config()->setAtNode<string>(
            PropertyName::currentDiagnosticsSlopeParamSelector(),
            (boost::format("%#x") % paramSelector).str());

    updateParameteSelectorWord(paramSelector);
}


void ServiceWorker::updateParameteSelectorWord(const uint32_t paramSelector)
{
    const std::string on= PropertyValue::on();
    const std::string off= PropertyValue::off();
    typedef ParamSelector PS;

    config()->setAtNode<string>(PropertyName::pbsBlockSelection(),
        paramSelector & PS::BLOCK_SELECTION ? "1" : "0");
    config()->setAtNode<string>(PropertyName::pbsEnableAcc(),
        paramSelector & PS::ENABLE_ACC ? on : off);
    config()->setAtNode<string>(PropertyName::pbsEnableAlternateRtrMode(),
        paramSelector & PS::ENABLE_ALTERNATE_RTR_MODE ? on : off);
    config()->setAtNode<string>(PropertyName::pbsEnableBcuSdram(),
        paramSelector & PS::ENABLE_BCU_SDRAM ? on : off);
    config()->setAtNode<string>(PropertyName::pbsEnableDspSdram(),
        paramSelector & PS::ENABLE_DSP_SDRAM ? on : off);
    config()->setAtNode<string>(PropertyName::pbsEnableFastLink(),
        paramSelector & PS::ENABLE_FAST_LINK ? on : off);
    // config()->setAtNode<string>(PropertyName::pbsEnableHvc(),
    //     paramSelector & PS::ENABLE_HVC ? on : off);
    config()->setAtNode<string>(PropertyName::pbsEnableMcf(),
        paramSelector & PS::ENABLE_MCF ? on : off);
    config()->setAtNode<string>(PropertyName::pbsEnableOvsDisturbance(),
        paramSelector & PS::ENABLE_OVS_DISTURBANCE ? on : off);
    config()->setAtNode<string>(PropertyName::pbsEnableWfsDisturbance(),
        paramSelector & PS::ENABLE_WFS_DISTURBANCE ? on : off);
}


void ServiceWorker::updateSlopeDiagnostics()
{
    const SeqOverallDiagnosticRecordPtr diagRecords = diagnosticsBuffer_->getPastSlopeOverallDiagnosticRecords(1);
    assert(0 != diagRecords);
    if(diagRecords->empty())
    {
        return;
    }

    const OverallDiagnosticRecord& firstRecord= diagRecords->at(0);
    assert(! firstRecord.slopeRecord.empty());
    assert(sizeof(SharknirBcuSlopeDiagnosticRecord) == firstRecord.slopeRecord.size());
    const SharknirBcuSlopeDiagnosticRecord* slopeRecord = reinterpret_cast<const SharknirBcuSlopeDiagnosticRecord*>(&firstRecord.slopeRecord.front());

    updateSlopeRecordParametersFrom(slopeRecord);
    updateFrameRateProperty(firstRecord);
    updateTipTiltDiagnostics(slopeRecord);
    updateFlaoFrameCounter(slopeRecord);
    updateNaFrameCounter(slopeRecord);
}


void ServiceWorker::updateNaFrameCounter(const SharknirBcuSlopeDiagnosticRecord *slopeRecord)
{
    const std::string naFcAsString= (boost::format("%u") % slopeRecord->sc_FCVector.na_FramesCounter).str();
    config()->setAtNode<string>(PropertyName::currentNaFrameCounter(), naFcAsString);
}


void ServiceWorker::updateFlaoFrameCounter(const SharknirBcuSlopeDiagnosticRecord *slopeRecord)
{
    const std::string fcAsString= (boost::format("%u") % slopeRecord->sc_FCVector.flao_FramesCounter).str();
    config()->setAtNode<string>(PropertyName::currentFlaoFrameCounter(), fcAsString);
}


void ServiceWorker::updateTipTiltDiagnostics(const SharknirBcuSlopeDiagnosticRecord *slopeRecord)
{
    updateTipTiltFrameCounter(slopeRecord);
    updateTipTiltReceivingRate(slopeRecord);
}


void ServiceWorker::updateTipTiltFrameCounter(const SharknirBcuSlopeDiagnosticRecord *slopeRecord)
{
    config()->setAtNode<string>(PropertyName::currentTipTiltFrameCounter(),
                                (boost::format("%u") % slopeRecord->sc_FCVector.mpifr_FrameNumber).str());
}


void ServiceWorker::updateTipTiltReceivingRate(const SharknirBcuSlopeDiagnosticRecord *slopeRecord)
{
    const Nice::Date now = clock_->getCurrentDate();
    const uint32_t currentFC= slopeRecord->sc_FCVector.mpifr_FrameNumber;
    if (latestTipTiltFCSnapshot_)
    {
        const uint32_t diffFC = currentFC - latestTipTiltFCSnapshot_->frameCounter;
        const Nice::Time elapsedTime = now - latestTipTiltFCSnapshot_->timestamp;
        const double rateHz = diffFC / elapsedTime.toSecondsDouble();
        const std::string rateHzAsString = (boost::format("%.2f") % rateHz).str();
        config()->setAtNode<string>(PropertyName::tipTiltReceivingRateHz(), rateHzAsString);
    }

    latestTipTiltFCSnapshot_= make_shared<TipTiltFrameCounterSnapshot>(currentFC, now);
}


void ServiceWorker::updateFrameRateProperty(const OverallDiagnosticRecord& record)
{
    const Nice::Date now = clock_->getCurrentDate();
    const FrameCounter latestFC = record.frameCounter;
    if (latestFrameCounterSnapshot_)
    {
        Nice::Time diff = Nice::Time(now - latestFrameCounterSnapshot_->timestamp);
        const double frameRateHz = (latestFC - latestFrameCounterSnapshot_->frameCounter) / diff.toSecondsDouble();
        config()->setAtNode<string>(PropertyName::frameRateHz(), (boost::format("%.1f") % frameRateHz).str());
    }
    else
    {
        latestFrameCounterSnapshot_= make_shared<FrameCounterSnapshot>();
    }
    latestFrameCounterSnapshot_->timestamp= now;
    latestFrameCounterSnapshot_->frameCounter= latestFC;
}


void ServiceWorker::updateLastestPnCCDImage()
{
    const SeqOverallDiagnosticRecordPtr ccdRecords = diagnosticsBuffer_->getPastCcdOverallDiagnosticRecords(1);
    if (ccdRecords->empty())
    {
        D_LOG("No CCD overall diagnostic record available.")
        return;
    }

    OverallDiagnosticRecord record= ccdRecords->at(0);
    if (record.ccdRecord.empty())
    {
        W_LOG("Diagnostics buffer provides empty CCD diagnostic record");
        return;
    }

    const size_t EXPECTED_CCD_RECORD_SIZE_IN_BYTE = sizeof(SharknirBcuCcdDiagnosticRecord);
    const size_t recordSizeInByte= record.ccdRecord.size();
    if (recordSizeInByte != EXPECTED_CCD_RECORD_SIZE_IN_BYTE)
    {
        W_LOG("CCD diagnostic record must have size of " << EXPECTED_CCD_RECORD_SIZE_IN_BYTE << " byte but has size of " << recordSizeInByte);
        return;
    }

    putCcdPixelsIntoProperty(record);
}

void ServiceWorker::putCcdPixelsIntoProperty(const OverallDiagnosticRecord& record)
{
    const SharknirBcuCcdDiagnosticRecord *rawRec = reinterpret_cast<const SharknirBcuCcdDiagnosticRecord *>(&record.ccdRecord.front());
    const uint16_t IMAGE_WIDTH_PIX= SharknirBcuCcdDiagnosticRecord::width;
    const uint16_t IMAGE_HEIGHT_PIX= SharknirBcuCcdDiagnosticRecord::height;
    Basda::ImageU16 img(Nice::Size(IMAGE_WIDTH_PIX, IMAGE_HEIGHT_PIX));
    // TODO 2013-09-24 Reorder pixels!
    assert(NULL != img.data());
    ::memcpy(img.data(), rawRec->pixels, IMAGE_WIDTH_PIX * IMAGE_HEIGHT_PIX * sizeof(SharknirBcuCcdDiagnosticRecord::Pixel));

    config()->setAtNode<Basda::ImageBase>(PropertyName::latestImage(), img);
}

void ServiceWorker::updateDiagnosticsCounterProperties()
{
    updateDiagnosticsCounterProperty(PropertyName::slopeRecordReceptionCounter(),
                                     diagnosticsBuffer_->getNumOfInsertedSlopeRecords());
    updateDiagnosticsCounterProperty(PropertyName::ccdRecordReceptionCounter(),
                                     diagnosticsBuffer_->getNumOfInsertedCcdRecords());
    // updateDiagnosticsCounterProperty(PropertyName::asmRecordReceptionCounter(),
    //         diagnosticsBuffer_->getNumOfInsertedAsmRecords());
    updateDiagnosticsCounterProperty(PropertyName::slopeRecordDuplicateReceptionCounter(),
                                     diagnosticsBuffer_->getNumOfDuplicatedSlopeRecords());
    updateDiagnosticsCounterProperty(PropertyName::ccdRecordDuplicateReceptionCounter(),
                                     diagnosticsBuffer_->getNumOfDuplicatedCcdRecords());
    // updateDiagnosticsCounterProperty(
    //         PropertyName::asmRecordDuplicateReceptionCounter(),
    //         diagnosticsBuffer_->getNumOfDuplicatedAsmRecords());
    updateDiagnosticsCounterProperty(PropertyName::highestReceivedFrameCounter(),
                                     diagnosticsBuffer_->getHighestReceivedFrameCounter());
    updateDiagnosticsCounterProperty(PropertyName::lateSlopeRecordCounter(),
                                     diagnosticsBuffer_->getNumOfLateSlopeRecords());
    updateDiagnosticsCounterProperty(PropertyName::lateCcdRecordCounter(),
                                     diagnosticsBuffer_->getNumOfLateCcdRecords());
    updateDiagnosticsCounterProperty(PropertyName::lateAsmRecordCounter(),
                                     diagnosticsBuffer_->getNumOfLateAsmRecords());
}


void ServiceWorker::updateDiagnosticsCounterProperty(const std::string& propertyName, const size_t counter)
{
    config()->setAtNode<int>(propertyName, static_cast<int>(counter));
}


void ServiceWorker::updateTemperatureProperty(const std::string& propertyName, double tempInC)
{
    static  Nice::Date last_alarm = clock_->getCurrentDate();

    const std::string doublePropertyName = PropertyName::doubleType(propertyName);
    config()->setAtNode<double>(doublePropertyName, tempInC);
    config()->setAtNode<string>(propertyName, lexical_cast<string>(tempInC));
    config()->leaf(doublePropertyName)->applyAttribute("IS_VALID", true);
    const double alarmThresholdInC = config()->leaf(doublePropertyName)->getAttribute<double>("ALARM");

    const Nice::Date now = clock_->getCurrentDate();
    const Nice::Time elapsedTime = now - last_alarm;

    if(alarmThresholdInC <= tempInC && elapsedTime > Nice::Time::seconds(60))
    {
        last_alarm = now;
        warnThatTemperatureExceedsLimit(propertyName, tempInC);
    }
}

void ServiceWorker::addDummyCcdImageProperty()
{
    Basda::ImageU16 image(Nice::Size(SharknirBcuCcdDiagnosticRecord::width, SharknirBcuCcdDiagnosticRecord::height));
    for (size_t x= 0; x < image.size().width(); ++x)
    {
        for (size_t y= 0; y < image.size().height(); ++y)
        {
            image.setPix(x, y, static_cast<uint16_t>(x+y));
        }
    }
    config()->setAtNode<Basda::ImageBase>(PropertyName::latestImage(), image);
}

void ServiceWorker::setDeviceAsUnreachable()
{
    device_= 0;
    config()->setAtNode<bool>(PropertyName::reachable(), false);

    invalidateTemperatureProperty(PropertyName::bcuFpgaTemperatureInC());
    invalidateTemperatureProperty(PropertyName::bcuPcbTemperatureInC());
    // invalidateTemperatureProperty(PropertyName::dsp16do0FpgaTemperatureInC());
    // invalidateTemperatureProperty(PropertyName::dsp16do0PcbTemperatureInC());
    // invalidateTemperatureProperty(PropertyName::dsp16do1FpgaTemperatureInC());
    // invalidateTemperatureProperty(PropertyName::dsp16do1PcbTemperatureInC());
    // invalidateTemperatureProperty(PropertyName::hvc0FpgaTemperatureInC());
    // invalidateTemperatureProperty(PropertyName::hvc0PcbTemperatureInC());
    // invalidateTemperatureProperty(PropertyName::hvc1FpgaTemperatureInC());
    // invalidateTemperatureProperty(PropertyName::hvc1PcbTemperatureInC());

    config()->setAtNode<string>(PropertyName::currentDiagnosticsSlopeFrameCounter(), "?");
    config()->setAtNode<string>(PropertyName::currentDiagnosticsSlopeParamSelector(), "?");

    latestFrameCounterSnapshot_.reset();
    diagnosticsBuffer_->clear();
    using namespace boost::assign;
    std::vector<string> propertyNames;
    propertyNames += PropertyName::frameRateHz(),
                    PropertyName::tipTiltReceivingRateHz(),
                    PropertyName::currentTipTiltFrameCounter(),
                    PropertyName::currentFlaoFrameCounter(),
                    PropertyName::currentNaFrameCounter(),
                    PropertyName::pbsBlockSelection(),
                    PropertyName::pbsEnableAcc(),
                    PropertyName::pbsEnableAlternateRtrMode(),
                    PropertyName::pbsEnableBcuSdram(),
                    PropertyName::pbsEnableDspSdram(),
                    // PropertyName::pbsEnableHvc(),
                    PropertyName::pbsEnableFastLink(),
                    PropertyName::pbsEnableMcf(),
                    PropertyName::pbsEnableOvsDisturbance(),
                    PropertyName::pbsEnableWfsDisturbance();

    BOOST_FOREACH(string each, propertyNames)
    {
        config()->setAtNode<string>(each, PropertyValue::unknown());
    }
}

void ServiceWorker::invalidateTemperatureProperty(const std::string & propertyName)
{
    const std::string doublePropertyName = PropertyName::doubleType(propertyName);
    config()->setAtNode<string>(propertyName, "?");
    if (! config()->exist(doublePropertyName))
    {
        config()->setAtNode<double>(doublePropertyName, -999.0);
        config()->leaf(doublePropertyName)->applyAttribute<double>("ALARM", 49.0);
        config()->leaf(doublePropertyName)->applyAttribute<double>("MIN", 10.0);
        config()->leaf(doublePropertyName)->applyAttribute<double>("MAX", 70.0);
    }
    config()->leaf(doublePropertyName)->applyAttribute("IS_VALID", false);
}


/**
 * @brief      Writes a command to bcu. Commands are expressed as OP code.
 *
 * @param[in]  opcode                The opcode, see Microgate manual for this BCU for reference
 * @param[in]  firstDsp              The first DSP. first DSP of the crate where the command should be actuated
 * @param[in]  lastDsp               The last DSP. first DSP of the crate where the command should be actuated
 * @param[in]  address               The address. Start address where data are written or read from
 * @param[in]  buffer                The buffer. Data to write to DSP
 * @param[in]  operationDescription  The operation description
 * @param[in]  propertyName          The property name in Nice tree
 */
void ServiceWorker::writeToBcu(Basda::Microgate::OpCode opcode,
        int firstDsp, int lastDsp, int32_t address,
        const Nice::SeqInt& buffer,
        const std::string& operationDescription,
        const std::string& propertyName)
{
    const size_t bufferSizeInDword= buffer.size();
    uint8_t validFirstDsp= boost::numeric_cast<uint8_t>(firstDsp);
    uint8_t validLastDsp= boost::numeric_cast<uint8_t>(lastDsp);

    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_ENTRY(operationDescription)

    MemoryBufferPtr b= MemoryBuffer::create(static_cast<unsigned>(bufferSizeInDword));
    int32_t* const bufferStart= b->getBeginOfBuffer();
    for (size_t i= 0; i < bufferSizeInDword; ++i)
    {
        bufferStart[i]= buffer[i];
    }

    U5_LOG("wrinting to device with MGP command " << opcode);
    getDevice()->write(opcode, validFirstDsp, validLastDsp, address, b);

    U5_LOG("setting number of written words to node " << propertyName);
    const int numOfAlreadyWrittenDwords= config()->getAtNode<int>(propertyName);
    config()->setAtNode<int>(propertyName, static_cast<int>(numOfAlreadyWrittenDwords + bufferSizeInDword));

    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_EXIT(operationDescription)
}


Nice::SeqInt ServiceWorker::readFromBcu(Basda::Microgate::OpCode opcode,
        int dspIndex, int32_t address, unsigned lengthInDword,
        const std::string& opDescription,
        const std::string& propertyName)
{
    const uint8_t validDspIndex= boost::numeric_cast<uint8_t>(dspIndex);
    Nice::SeqInt result;

    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_ENTRY(opDescription)

    Basda::Microgate::MemoryBufferPtr b= getDevice()->read(
        opcode, validDspIndex, validDspIndex,
        address, lengthInDword);

    const int numOfReadBackDwords= b->getBufferLengthInDword();
    for (int i= 0; i < numOfReadBackDwords; ++i)
    {
        result.push_back(b->getBeginOfBuffer()[i]);
    }

    const int numOfAlreadyReadBackDwords=
            config()->getAtNode<int>(propertyName);
    config()->setAtNode<int>(propertyName,
            numOfAlreadyReadBackDwords + numOfReadBackDwords);

    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_EXIT(opDescription)

    return result;
}


/**
 * @brief      Gets a pointer to the device.
 *
 * @return     The device pointer as DevicePtr.
 */
DevicePtr ServiceWorker::getDevice()
{
  SHARKNIR_PRECONDITION(0 != deviceFactory_, "deviceFactory is valid");
  if (0 == device_)
  {
      device_= deviceFactory_->buildDevice();
  }

  SHARKNIR_POSTCONDITION(0 != device_, "device is valid");
  return device_;
}

void ServiceWorker::checkNumOfRequestedOverallDiagnosticRecords(int nRecords)
{
    if (0 == nRecords)
    {
        E_THROW(Sharknir::BcuServiceException,
                "Zero diagnostics records requested")
    }

    const size_t USER_LORENZO_LIMIT= 4000;
    const int RECORD_LIMIT= static_cast<int>(
            std::min(USER_LORENZO_LIMIT, diagnosticsBuffer_->getCapacity()));
    if (RECORD_LIMIT < nRecords)
    {
        E_THROW(Sharknir::BcuServiceException, "Amount of requested records (" <<
                nRecords << ") exceeds limit of " << RECORD_LIMIT << ".")
    }
}

void ServiceWorker::checkDiagnosticRecordAvailability(const int nRecords)
{
    const size_t diagBufferSize = diagnosticsBuffer_->getSize();
    if (nRecords > static_cast<int>(diagBufferSize))
    {
        E_THROW(Sharknir::BcuServiceException, "Number of requested records(" <<
                nRecords << ") exceeds number of"
                " available diagnostics records (" << diagBufferSize << ").")
    }
}

void ServiceWorker::collectDiagnosticsSnapshotGarbage()
{
    typedef std::pair<Cookie, OverallDiagnosticRecordSeqSnapshot> SnapshotPair;
    Nice::Date now = clock_->getCurrentDate();
    std::vector<Cookie> staleCookies;
    BOOST_FOREACH(SnapshotPair p, diagnosticsSnapshots_)
    {
        if(p.second.timestamp_ + diagnosticsSnapshotTimeout_ < now)
        {
            staleCookies.push_back(p.first);
        }
    }

    BOOST_FOREACH(Cookie c, staleCookies)
    {
        const size_t nElements= diagnosticsSnapshots_.erase(c);
        assert(1 == nElements);
    }
}

void ServiceWorker::addRecordsToSnapshot(const Cookie& cookie, const SeqOverallDiagnosticRecordPtr& records)
{
    OverallDiagnosticRecordSeqSnapshot snapshot;
    snapshot.timestamp_ = clock_->getCurrentDate();
    snapshot.overallRecords_ = records;
    diagnosticsSnapshots_[cookie]= snapshot;
}

Sharknir::Bcu::SeqOverallDiagnosticRecord ServiceWorker::getOverallDiagnosticRecordsFromSnapshot(const Cookie& cookie, const int startRecordIndex, const int nRecords)
{
    SeqOverallDiagnosticRecord result;

    if (diagnosticsSnapshots_.end() == diagnosticsSnapshots_.find(cookie))
    {
        E_THROW(Sharknir::BcuServiceException, std::string("Unregistered cookie for getting overall diagnostics records: ") + cookie );
    }
    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_ENTRY("getOverallDiagnosticRecordsFromSnapshot")

    SeqOverallDiagnosticRecordPtr overallRecords = diagnosticsSnapshots_[cookie].overallRecords_;
    result.reserve(nRecords);
    for (int i= 0; i < nRecords; ++i)
    {
        result.push_back(overallRecords->at(startRecordIndex+i));
    }

    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_EXIT("getOverallDiagnosticRecordsFromSnapshot")
    return result;
}

void ServiceWorker::freeOverallDiagnosticRecordSnapshot(const Cookie& cookie)
{
    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_ENTRY("freeOverallDiagnosticRecordSnapshot")

    if (diagnosticsSnapshots_.end() == diagnosticsSnapshots_.find(cookie))
    {
        return;
    }
    const size_t nErasedSnapshots= diagnosticsSnapshots_.erase(cookie);
    assert(1 == nErasedSnapshots);

    SHARKNIR_BCU_SERVICEWORKER_EXTERNAL_OPERATION_EXIT("freeOverallDiagnosticRecordSnapshot")
}

void ServiceWorker::keepHouse()
{
    D_LOG(__FUNCTION__ << " Happily running");
    Nice::Guard lock(deviceMutex_);
    try
    {
        collectDiagnosticsSnapshotGarbage();
        updateProperties();
        config()->setAtNode<bool>(PropertyName::reachable(), true);
        communicationGlitchCount_= 0;
    }
    catch (Basda::Microgate::MgpException& e)
    {
        if (++communicationGlitchCount_ > nMaxConsecutiveCommunicationGlitches_)
        {
            setDeviceAsUnreachable();
            E_LOG("Number of communication failures exceeds"
              " tolerance limit of " <<
              nMaxConsecutiveCommunicationGlitches_ << "." <<
              " Reason for last failure was: " << e.what());
        }
        else
        {
            W_LOG(e.what());
        }
    }
    catch (std::exception& e)
    {
        setDeviceAsUnreachable();
        E_LOG("Failed to update properties: " << e.what());
    }
}

}
}
