#include "Module.h"
#include "SharedMemory.h"
#include "sls/ClientSocket.h"
#include "sls/ToString.h"
#include "sls/bit_utils.h"
#include "sls/container_utils.h"
#include "sls/file_utils.h"
#include "sls/md5_helper.h"
#include "sls/network_utils.h"
#include "sls/sls_detector_exceptions.h"
#include "sls/sls_detector_funcs.h"
#include "sls/string_utils.h"
#include "sls/versionAPI.h"

#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <iomanip>
#include <iterator>
#include <sstream>
#include <thread>

namespace sls {

// creating new shm
Module::Module(detectorType type, int det_id, int module_index, bool verify)
    : moduleIndex(module_index), shm(det_id, module_index) {

    // ensure shared memory was not created before
    if (shm.IsExisting()) {
        LOG(logWARNING) << "This shared memory should have been "
                           "deleted before! "
                        << shm.GetName() << ". Freeing it again";
        shm.RemoveSharedMemory();
    }

    initSharedMemory(type, det_id, verify);
}

// opening existing shm
Module::Module(int det_id, int module_index, bool verify)
    : moduleIndex(module_index), shm(det_id, module_index) {

    // getDetectorType From shm will check if existing
    detectorType type = getDetectorTypeFromShm(det_id, verify);
    initSharedMemory(type, det_id, verify);
}

Module::~Module() = default;

void Module::freeSharedMemory() {
    if (shm.IsExisting()) {
        shm.RemoveSharedMemory();
    }
}

bool Module::isFixedPatternSharedMemoryCompatible() const {
    return (shm()->shmversion >= MODULE_SHMAPIVERSION);
}

std::string Module::getHostname() const { return shm()->hostname; }

void Module::setHostname(const std::string &hostname,
                         const bool initialChecks) {
    sls::strcpy_safe(shm()->hostname, hostname.c_str());
    auto client = DetectorSocket(shm()->hostname, shm()->controlPort);
    client.close();
    try {
        checkDetectorVersionCompatibility();
        LOG(logINFO) << "Module Version Compatibility - Success";
    } catch (const DetectorError &e) {
        if (!initialChecks) {
            LOG(logWARNING) << "Bypassing Initial Checks at your own risk!";
        } else {
            throw;
        }
    }
    if (shm()->detType == EIGER) {
        setActivate(true);
    }
}

int64_t Module::getFirmwareVersion() const {
    return sendToDetector<int64_t>(F_GET_FIRMWARE_VERSION);
}

int64_t Module::getDetectorServerVersion() const {
    return sendToDetector<int64_t>(F_GET_SERVER_VERSION);
}

int64_t Module::getSerialNumber() const {
    return sendToDetector<int64_t>(F_GET_SERIAL_NUMBER);
}

int Module::getModuleId() const { return sendToDetector<int>(F_GET_MODULE_ID); }

int64_t Module::getReceiverSoftwareVersion() const {
    if (shm()->useReceiverFlag) {
        return sendToReceiver<int64_t>(F_GET_RECEIVER_VERSION);
    }
    return -1;
}

// static function
slsDetectorDefs::detectorType
Module::getTypeFromDetector(const std::string &hostname, int cport) {
    LOG(logDEBUG1) << "Getting Module type ";
    sls::ClientSocket socket("Detector", hostname, cport);
    socket.Send(F_GET_DETECTOR_TYPE);
    socket.Receive<int>(); // TODO! Should we look at this OK/FAIL?
    auto retval = socket.Receive<detectorType>();
    LOG(logDEBUG1) << "Module type is " << retval;
    return retval;
}

slsDetectorDefs::detectorType Module::getDetectorType() const {
    return shm()->detType;
}

void Module::updateNumberOfChannels() {
    if (shm()->detType == CHIPTESTBOARD || shm()->detType == MOENCH) {
        std::array<int, 2> retvals{};
        sendToDetector(F_GET_NUM_CHANNELS, nullptr, retvals);
        shm()->nChan.x = retvals[0];
        shm()->nChan.y = retvals[1];
    }
}

slsDetectorDefs::xy Module::getNumberOfChannels() const {
    slsDetectorDefs::xy coord{};
    coord.x = (shm()->nChan.x * shm()->nChip.x);
    coord.y = (shm()->nChan.y * shm()->nChip.y);
    return coord;
}

void Module::updateNumberOfModule(slsDetectorDefs::xy det) {
    shm()->numberOfModule = det;
    int args[2] = {shm()->numberOfModule.y, moduleIndex};
    sendToDetector(F_SET_POSITION, args, nullptr);
}

slsDetectorDefs::detectorSettings Module::getSettings() const {
    return sendToDetector<detectorSettings>(F_SET_SETTINGS, GET_FLAG);
}

void Module::setSettings(detectorSettings isettings) {
    if (shm()->detType == EIGER) {
        throw RuntimeError(
            "Cannot set settings for Eiger. Use threshold energy.");
    }
    sendToDetector<int>(F_SET_SETTINGS, isettings);
}

int Module::getThresholdEnergy() const {
    return sendToDetector<int>(F_GET_THRESHOLD_ENERGY);
}

std::array<int, 3> Module::getAllThresholdEnergy() const {
    return sendToDetector<std::array<int, 3>>(F_GET_ALL_THRESHOLD_ENERGY);
}

void Module::setThresholdEnergy(int e_eV, detectorSettings isettings,
                                bool trimbits) {

    // verify e_eV exists in trimEneregies[]
    if (shm()->trimEnergies.empty() || (e_eV < shm()->trimEnergies.front()) ||
        (e_eV > shm()->trimEnergies.back())) {
        throw RuntimeError("This energy " + std::to_string(e_eV) +
                           " not defined for this module!");
    }
    bool interpolate =
        std::all_of(shm()->trimEnergies.begin(), shm()->trimEnergies.end(),
                    [e_eV](const int &e) { return e != e_eV; });

    sls_detector_module myMod{shm()->detType};

    if (!interpolate) {
        std::string settingsfname = getTrimbitFilename(isettings, e_eV);
        LOG(logDEBUG1) << "Settings File is " << settingsfname;
        myMod = readSettingsFile(settingsfname, trimbits);
    } else {
        // find the trim values
        int trim1 = -1, trim2 = -1;
        for (size_t i = 0; i < shm()->trimEnergies.size(); ++i) {
            if (e_eV < shm()->trimEnergies[i]) {
                trim2 = shm()->trimEnergies[i];
                trim1 = shm()->trimEnergies[i - 1];
                break;
            }
        }
        std::string settingsfname1 = getTrimbitFilename(isettings, trim1);
        std::string settingsfname2 = getTrimbitFilename(isettings, trim2);
        LOG(logDEBUG1) << "Settings Files are " << settingsfname1 << " and "
                       << settingsfname2;
        auto myMod1 = readSettingsFile(settingsfname1, trimbits);
        auto myMod2 = readSettingsFile(settingsfname2, trimbits);
        if (myMod1.iodelay != myMod2.iodelay) {
            throw RuntimeError("setThresholdEnergyAndSettings: Iodelays do not "
                               "match between files");
        }
        myMod = interpolateTrim(&myMod1, &myMod2, e_eV, trim1, trim2, trimbits);
        myMod.iodelay = myMod1.iodelay;
        myMod.tau =
            linearInterpolation(e_eV, trim1, trim2, myMod1.tau, myMod2.tau);
    }

    myMod.reg = isettings;
    myMod.eV[0] = e_eV;
    setModule(myMod, trimbits);
    if (getSettings() != isettings) {
        throw RuntimeError("setThresholdEnergyAndSettings: Could not set "
                           "settings in Module");
    }

    if (shm()->useReceiverFlag) {
        sendToReceiver(F_RECEIVER_SET_THRESHOLD, e_eV, nullptr);
    }
}

void Module::setAllThresholdEnergy(std::array<int, 3> e_eV,
                                   detectorSettings isettings, bool trimbits) {
    if (shm()->trimEnergies.empty()) {
        throw RuntimeError(
            "Trim energies have not been defined for this module yet!");
    }

    auto counters = getSetBits(getCounterMask());
    enum mythen3_DacIndex {
        M_VCASSH,
        M_VTH2,
        M_VRSHAPER,
        M_VRSHAPER_N,
        M_VIPRE_OUT,
        M_VTH3,
        M_VTH1,
        M_VICIN,
        M_VCAS,
        M_VRPREAMP,
        M_VCAL_N,
        M_VIPRE,
        M_VISHAPER,
        M_VCAL_P,
        M_VTRIM,
        M_VDCSH
    };

    std::vector<sls_detector_module> myMods{shm()->detType};
    std::vector<int> energy(e_eV.begin(), e_eV.end());
    // if all energies are same
    if (allEqualTo(energy, energy[0])) {
        energy.resize(1);
    }
    myMods.resize(energy.size());

    // for each threshold
    for (size_t i = 0; i < energy.size(); ++i) {
        // don't interpolate
        if (shm()->trimEnergies.anyEqualTo(energy[i])) {
            std::string settingsfname =
                getTrimbitFilename(isettings, energy[i]);
            LOG(logDEBUG1) << "Settings File is " << settingsfname;
            myMods[i] = readSettingsFile(settingsfname, trimbits);
        }
        // interpolate
        else {
            if (shm()->trimEnergies.size() < 2) {
                throw RuntimeError(
                    "Cannot interpolate with less than 2 trim energies");
            }
            // find the trim values
            int trim1 = -1, trim2 = -1;
            if (energy[i] < shm()->trimEnergies[0]) {
                trim1 = shm()->trimEnergies[0];
                trim2 = shm()->trimEnergies[1];
            } else if (energy[i] >
                       shm()->trimEnergies[shm()->trimEnergies.size() - 1]) {
                trim1 = shm()->trimEnergies[shm()->trimEnergies.size() - 2];
                trim2 = shm()->trimEnergies[shm()->trimEnergies.size() - 1];
            } else {
                for (size_t j = 0; j < shm()->trimEnergies.size(); ++j) {
                    // std::cout << "checking " << energy[i] << " and  "
                    //         << shm()->trimEnergies[j] << std::endl;
                    if (energy[i] < shm()->trimEnergies[j]) {
                        trim1 = shm()->trimEnergies[j - 1];
                        trim2 = shm()->trimEnergies[j];
                        break;
                    }
                }
            }
            LOG(logINFO) << "e_eV:" << energy[i] << " [" << trim1 << ", "
                         << trim2 << "]";

            std::string settingsfname1 = getTrimbitFilename(isettings, trim1);
            std::string settingsfname2 = getTrimbitFilename(isettings, trim2);
            LOG(logDEBUG1) << "Settings Files are " << settingsfname1 << " and "
                           << settingsfname2;
            auto myMod1 = readSettingsFile(settingsfname1, trimbits);
            auto myMod2 = readSettingsFile(settingsfname2, trimbits);

            myMods[i] = interpolateTrim(&myMod1, &myMod2, energy[i], trim1,
                                        trim2, trimbits);
        }
    }

    sls_detector_module myMod{shm()->detType};
    myMod = myMods[0];

    // if multiple thresholds, combine
    if (myMods.size() > 1) {

        // average vtrim of enabled counters
        int sum = 0;
        for (size_t i = 0; i < counters.size(); ++i) {
            sum += myMods[counters[i]].dacs[M_VTRIM];
        }
        myMod.dacs[M_VTRIM] = sum / counters.size();

        // copy vth1, vth2 and vth3 from the correct threshold mods
        myMod.dacs[M_VTH1] = myMods[0].dacs[M_VTH1];
        myMod.dacs[M_VTH2] = myMods[1].dacs[M_VTH2];
        myMod.dacs[M_VTH3] = myMods[2].dacs[M_VTH3];

        // check if dacs are different
        for (size_t j = 0; j < 16; ++j) {
            if (j == M_VTRIM || j == M_VTH1 || j == M_VTH2 || j == M_VTH3)
                continue;

            if (myMods[0].dacs[j] != myMods[1].dacs[j]) {
                throw RuntimeError("Dac Index " + std::to_string(j) +
                                   " differs for threshold[0]:[" +
                                   std::to_string(myMods[0].dacs[j]) +
                                   "] and threshold[1]:" +
                                   std::to_string(myMods[1].dacs[j]) + "]");
            }
            if (myMods[1].dacs[j] != myMods[2].dacs[j]) {
                throw RuntimeError("Dac Index " + std::to_string(j) +
                                   " differs for threshold[1]:[" +
                                   std::to_string(myMods[1].dacs[j]) +
                                   "] and threshold[2]:" +
                                   std::to_string(myMods[2].dacs[j]) + "]");
            }
        }

        // replace correct trim values (interleaved)
        for (int i = 0; i < myMod.nchan; ++i) {
            myMod.chanregs[i] = myMods[i % 3].chanregs[i];
        }
    }

    myMod.reg = isettings;
    std::copy(e_eV.begin(), e_eV.end(), myMod.eV);
    LOG(logDEBUG) << "ev:" << ToString(myMod.eV);

    // check for trimbits that are out of range
    bool out_of_range = false;
    for (int i = 0; i != myMod.nchan; ++i) {
        if (myMod.chanregs[i] < 0) {
            myMod.chanregs[i] = 0;
            out_of_range = true;
        } else if (myMod.chanregs[i] > 63) {
            myMod.chanregs[i] = 63;
            out_of_range = true;
        }
    }
    if (out_of_range) {
        LOG(logWARNING)
            << "Some trimbits were out of range after interpolation, these "
               "have been replaced with 0 or 63.";
    }

    // check dacs
    out_of_range = false;
    for (auto dac : {M_VTRIM, M_VTH1, M_VTH2, M_VTH3}) {
        if (myMod.dacs[dac] < 600) {
            myMod.dacs[dac] = 600;
            out_of_range = true;
        } else if (myMod.dacs[dac] > 2400) {
            myMod.dacs[dac] = 2400;
            out_of_range = true;
        }
    }
    if (out_of_range) {
        LOG(logWARNING) << "Some dacs were out of range after interpolation, "
                           "these have been replaced with 600 or 2400.";
    }

    setModule(myMod, trimbits);
    if (getSettings() != isettings) {
        throw RuntimeError("setThresholdEnergyAndSettings: Could not set "
                           "settings in Module");
    }

    if (shm()->useReceiverFlag) {
        sendToReceiver(F_RECEIVER_SET_ALL_THRESHOLD, e_eV, nullptr);
    }
}

std::string Module::getSettingsDir() const {
    return std::string(shm()->settingsDir);
}

std::string Module::setSettingsDir(const std::string &dir) {
    sls::strcpy_safe(shm()->settingsDir, dir.c_str());
    return shm()->settingsDir;
}

void Module::loadTrimbits(const std::string &fname) {
    // find specific file if it has detid in file name (.snxxx)
    if (shm()->detType == EIGER || shm()->detType == MYTHEN3) {
        std::ostringstream ostfn;
        ostfn << fname;
        int moduleIdWidth = 3;
        if (shm()->detType == MYTHEN3) {
            moduleIdWidth = 4;
        }
        if ((fname.find(".sn") == std::string::npos) &&
            (fname.find(".trim") == std::string::npos)) {
            ostfn << ".sn" << std::setfill('0') << std::setw(moduleIdWidth)
                  << std::dec << getModuleId();
        }
        auto myMod = readSettingsFile(ostfn.str());
        setModule(myMod);
    } else {
        throw RuntimeError("not implemented for this detector");
    }
}

int Module::getAllTrimbits() const {
    return sendToDetector<int>(F_SET_ALL_TRIMBITS, GET_FLAG);
}

void Module::setAllTrimbits(int val) {
    sendToDetector<int>(F_SET_ALL_TRIMBITS, val);
}

std::vector<int> Module::getTrimEn() const {
    if (shm()->detType != EIGER && shm()->detType != MYTHEN3) {
        throw RuntimeError("getTrimEn not implemented for this detector.");
    }
    return std::vector<int>(shm()->trimEnergies.begin(),
                            shm()->trimEnergies.end());
}

int Module::setTrimEn(const std::vector<int> &energies) {
    if (shm()->detType != EIGER && shm()->detType != MYTHEN3) {
        throw RuntimeError("setTrimEn not implemented for this detector.");
    }
    if (energies.size() > MAX_TRIMEN) {
        std::ostringstream os;
        os << "Size of trim energies: " << energies.size()
           << " exceeds what can be stored in shared memory: " << MAX_TRIMEN
           << "\n";
        throw RuntimeError(os.str());
    }
    shm()->trimEnergies = energies;
    std::sort(shm()->trimEnergies.begin(), shm()->trimEnergies.end());
    return shm()->trimEnergies.size();
}

bool Module::getFlipRows() const {
    if (shm()->detType == EIGER) {
        return sendToReceiver<int>(F_GET_FLIP_ROWS_RECEIVER);
    }
    return sendToDetector<int>(F_GET_FLIP_ROWS);
}

void Module::setFlipRows(bool value) {
    if (shm()->detType == EIGER) {
        sendToReceiver<int>(F_SET_FLIP_ROWS_RECEIVER, static_cast<int>(value));
    } else {
        sendToDetector(F_SET_FLIP_ROWS, static_cast<int>(value), nullptr);
    }
}

bool Module::isVirtualDetectorServer() const {
    return sendToDetector<int>(F_IS_VIRTUAL);
}

int64_t Module::getNumberOfFrames() const {
    return sendToDetector<int64_t>(F_GET_NUM_FRAMES);
}

void Module::setNumberOfFrames(int64_t value) {
    sendToDetector(F_SET_NUM_FRAMES, value, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_RECEIVER_SET_NUM_FRAMES, value, nullptr);
    }
}

int64_t Module::getNumberOfTriggers() const {
    return sendToDetector<int64_t>(F_GET_NUM_TRIGGERS);
}

void Module::setNumberOfTriggers(int64_t value) {
    sendToDetector(F_SET_NUM_TRIGGERS, value, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_SET_RECEIVER_NUM_TRIGGERS, value, nullptr);
    }
}

int64_t Module::getExptime(int gateIndex) const {
    return sendToDetector<int64_t>(F_GET_EXPTIME, gateIndex);
}

void Module::setExptime(int gateIndex, int64_t value) {
    int64_t prevVal = value;
    if (shm()->detType == EIGER) {
        prevVal = getExptime(-1);
    }
    int64_t args[]{static_cast<int64_t>(gateIndex), value};
    sendToDetector(F_SET_EXPTIME, args, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_RECEIVER_SET_EXPTIME, args, nullptr);
    }
    if (prevVal != value) {
        updateRateCorrection();
    }
}

int64_t Module::getPeriod() const {
    return sendToDetector<int64_t>(F_GET_PERIOD);
}

void Module::setPeriod(int64_t value) {
    sendToDetector(F_SET_PERIOD, value, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_RECEIVER_SET_PERIOD, value, nullptr);
    }
}

int64_t Module::getDelayAfterTrigger() const {
    return sendToDetector<int64_t>(F_GET_DELAY_AFTER_TRIGGER);
}

void Module::setDelayAfterTrigger(int64_t value) {
    sendToDetector(F_SET_DELAY_AFTER_TRIGGER, value, nullptr);
}

int64_t Module::getNumberOfFramesLeft() const {
    return sendToDetectorStop<int64_t>(F_GET_FRAMES_LEFT);
}

int64_t Module::getNumberOfTriggersLeft() const {
    return sendToDetectorStop<int64_t>(F_GET_TRIGGERS_LEFT);
}

int64_t Module::getDelayAfterTriggerLeft() const {
    return sendToDetectorStop<int64_t>(F_GET_DELAY_AFTER_TRIGGER_LEFT);
}

int64_t Module::getPeriodLeft() const {
    return sendToDetectorStop<int64_t>(F_GET_PERIOD_LEFT);
}

int Module::getDynamicRange() const {
    return sendToDetector<int>(F_SET_DYNAMIC_RANGE, GET_FLAG);
}

void Module::setDynamicRange(int dr) {
    int prev_val = dr;
    if (shm()->detType == EIGER) {
        prev_val = getDynamicRange();
    }

    auto retval = sendToDetector<int>(F_SET_DYNAMIC_RANGE, dr);
    if (shm()->useReceiverFlag) {
        sendToReceiver<int>(F_SET_RECEIVER_DYNAMIC_RANGE, retval);
    }

    // update speed
    if (shm()->detType == EIGER) {
        if (dr == 32) {
            LOG(logINFO) << "Setting Clock to Quarter Speed to cope with "
                            "Dynamic Range of 32";
            setClockDivider(RUN_CLOCK, 2);
        } else {
            LOG(logINFO) << "Setting Clock to Full Speed for Dynamic Range of "
                         << dr;
            setClockDivider(RUN_CLOCK, 0);
        }
        // EIGER only, update speed and rate correction when dr changes
        if (dr != prev_val) {
            updateRateCorrection();
        }
    }
}

slsDetectorDefs::timingMode Module::getTimingMode() const {
    return sendToDetector<timingMode>(F_SET_TIMING_MODE, GET_FLAG);
}

void Module::setTimingMode(timingMode value) {
    sendToDetector<int>(F_SET_TIMING_MODE, value);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_SET_RECEIVER_TIMING_MODE, value, nullptr);
    }
}

int Module::getClockDivider(int clkIndex) const {
    return sendToDetector<int>(F_GET_CLOCK_DIVIDER, clkIndex);
}

void Module::setClockDivider(int clkIndex, int value) {
    int args[]{clkIndex, value};
    sendToDetector(F_SET_CLOCK_DIVIDER, args, nullptr);
}

int Module::getClockPhase(int clkIndex, bool inDegrees) const {
    int args[]{clkIndex, static_cast<int>(inDegrees)};
    return sendToDetector<int>(F_GET_CLOCK_PHASE, args);
}

void Module::setClockPhase(int clkIndex, int value, bool inDegrees) {
    int args[]{clkIndex, value, static_cast<int>(inDegrees)};
    sendToDetector(F_SET_CLOCK_PHASE, args, nullptr);
}

int Module::getMaxClockPhaseShift(int clkIndex) const {
    return sendToDetector<int>(F_GET_MAX_CLOCK_PHASE_SHIFT, clkIndex);
}

int Module::getClockFrequency(int clkIndex) const {
    return sendToDetector<int>(F_GET_CLOCK_FREQUENCY, clkIndex);
}

void Module::setClockFrequency(int clkIndex, int value) {
    int args[]{clkIndex, value};
    sendToDetector(F_SET_CLOCK_FREQUENCY, args, nullptr);
}

int Module::getDAC(dacIndex index, bool mV) const {
    int args[]{static_cast<int>(index), static_cast<int>(mV), GET_FLAG};
    return sendToDetector<int>(F_SET_DAC, args);
}
int Module::getDefaultDac(slsDetectorDefs::dacIndex index,
                          slsDetectorDefs::detectorSettings sett) {
    int args[]{static_cast<int>(index), static_cast<int>(sett)};
    return sendToDetector<int>(F_GET_DEFAULT_DAC, args);
}
void Module::setDefaultDac(slsDetectorDefs::dacIndex index, int defaultValue,
                           defs::detectorSettings sett) {
    int args[]{static_cast<int>(index), static_cast<int>(sett), defaultValue};
    return sendToDetector(F_SET_DEFAULT_DAC, args, nullptr);
}

void Module::resetToDefaultDacs(const bool hardReset) {
    sendToDetector(F_RESET_TO_DEFAULT_DACS, static_cast<int>(hardReset),
                   nullptr);
}

void Module::setDAC(int val, dacIndex index, bool mV) {
    int args[]{static_cast<int>(index), static_cast<int>(mV), val};
    sendToDetector<int>(F_SET_DAC, args);
}

bool Module::getPowerChip() const {
    return sendToDetector<int>(F_POWER_CHIP, GET_FLAG);
}

void Module::setPowerChip(bool on) {
    sendToDetector<int>(F_POWER_CHIP, static_cast<int>(on));
}

int Module::getImageTestMode() const {
    return sendToDetector<int>(F_GET_IMAGE_TEST_MODE);
}

void Module::setImageTestMode(const int value) {
    sendToDetector(F_SET_IMAGE_TEST_MODE, value, nullptr);
}

int Module::getADC(dacIndex index) const {
    return sendToDetectorStop<int>(F_GET_ADC, index);
}

int Module::getOnChipDAC(slsDetectorDefs::dacIndex index, int chipIndex) const {
    int args[]{static_cast<int>(index), chipIndex};
    return sendToDetector<int>(F_GET_ON_CHIP_DAC, args);
}

void Module::setOnChipDAC(slsDetectorDefs::dacIndex index, int chipIndex,
                          int value) {
    int args[]{static_cast<int>(index), chipIndex, value};
    sendToDetector(F_SET_ON_CHIP_DAC, args, nullptr);
}

slsDetectorDefs::externalSignalFlag
Module::getExternalSignalFlags(int signalIndex) const {
    return sendToDetector<slsDetectorDefs::externalSignalFlag>(
        F_GET_EXTERNAL_SIGNAL_FLAG, signalIndex);
}

void Module::setExternalSignalFlags(int signalIndex, externalSignalFlag type) {
    int args[]{signalIndex, static_cast<int>(type)};
    sendToDetector(F_SET_EXTERNAL_SIGNAL_FLAG, args, nullptr);
}

bool Module::getParallelMode() const {
    return sendToDetector<int>(F_GET_PARALLEL_MODE);
}

void Module::setParallelMode(const bool enable) {
    sendToDetector(F_SET_PARALLEL_MODE, static_cast<int>(enable), nullptr);
}

int Module::getFilterResistor() const {
    return sendToDetector<int>(F_GET_FILTER_RESISTOR);
}

void Module::setFilterResistor(int value) {
    sendToDetector(F_SET_FILTER_RESISTOR, value, nullptr);
}

defs::currentSrcParameters Module::getCurrentSource() const {
    return sendToDetector<defs::currentSrcParameters>(F_GET_CURRENT_SOURCE);
}

void Module::setCurrentSource(defs::currentSrcParameters par) {
    sendToDetector(F_SET_CURRENT_SOURCE, par, nullptr);
}

int Module::getDBITPipeline() const {
    return sendToDetector<int>(F_GET_DBIT_PIPELINE);
}

void Module::setDBITPipeline(int value) {
    sendToDetector(F_SET_DBIT_PIPELINE, value, nullptr);
}

int Module::getReadNRows() const {
    return sendToDetector<int>(F_GET_READ_N_ROWS);
}

void Module::setReadNRows(const int value) {
    sendToDetector(F_SET_READ_N_ROWS, value, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_SET_RECEIVER_READ_N_ROWS, value, nullptr);
    }
}

// Acquisition

void Module::startReceiver() {
    shm()->stoppedFlag = false;
    sendToReceiver(F_START_RECEIVER);
}

void Module::stopReceiver() {
    sendToReceiver(F_STOP_RECEIVER, static_cast<int>(shm()->stoppedFlag),
                   nullptr);
}

void Module::startAcquisition() {
    shm()->stoppedFlag = false;
    sendToDetector(F_START_ACQUISITION);
}

void Module::startReadout() {
    shm()->stoppedFlag = false;
    sendToDetector(F_START_READOUT);
}

void Module::stopAcquisition() {
    // get status before stopping acquisition
    runStatus s = ERROR, r = ERROR;
    bool zmqstreaming = false;
    try {
        if (shm()->useReceiverFlag && getReceiverStreaming()) {
            zmqstreaming = true;
            s = getRunStatus();
            r = getReceiverStatus();
        }
    } catch (...) {
        // if receiver crashed, stop detector in any case
        zmqstreaming = false;
    }

    sendToDetectorStop(F_STOP_ACQUISITION);
    shm()->stoppedFlag = true;

    // if rxr streaming and acquisition finished, restream dummy stop packet
    if (zmqstreaming && (s == IDLE) && (r == IDLE)) {
        restreamStopFromReceiver();
    }
}

void Module::restreamStopFromReceiver() {
    sendToReceiver(F_RESTREAM_STOP_FROM_RECEIVER);
}

void Module::startAndReadAll() {
    shm()->stoppedFlag = false;
    sendToDetector(F_START_AND_READ_ALL);
}

slsDetectorDefs::runStatus Module::getRunStatus() const {
    return sendToDetectorStop<runStatus>(F_GET_RUN_STATUS);
}

slsDetectorDefs::runStatus Module::getReceiverStatus() const {
    return sendToReceiver<runStatus>(F_GET_RECEIVER_STATUS);
}

double Module::getReceiverProgress() const {
    return sendToReceiver<double>(F_GET_RECEIVER_PROGRESS);
}

int64_t Module::getFramesCaughtByReceiver() const {
    return sendToReceiver<int64_t>(F_GET_RECEIVER_FRAMES_CAUGHT);
}

std::vector<uint64_t> Module::getNumMissingPackets() const {
    // TODO!(Erik) Refactor
    LOG(logDEBUG1) << "Getting num missing packets";
    if (shm()->useReceiverFlag) {
        auto client = ReceiverSocket(shm()->rxHostname, shm()->rxTCPPort);
        client.Send(F_GET_NUM_MISSING_PACKETS);
        if (client.Receive<int>() == FAIL) {
            throw RuntimeError("Receiver " + std::to_string(moduleIndex) +
                               " returned error: " + client.readErrorMessage());
        } else {
            auto nports = client.Receive<int>();
            std::vector<uint64_t> retval(nports);
            client.Receive(retval);
            LOG(logDEBUG1) << "Missing packets of Receiver" << moduleIndex << ": "
                           << sls::ToString(retval);
            return retval;
        }
    }
    throw RuntimeError("No receiver to get missing packets.");
}

uint64_t Module::getNextFrameNumber() const {
    return sendToDetector<uint64_t>(F_GET_NEXT_FRAME_NUMBER);
}

void Module::setNextFrameNumber(uint64_t value) {
    sendToDetector(F_SET_NEXT_FRAME_NUMBER, value, nullptr);
}

void Module::sendSoftwareTrigger(const bool block) {
    sendToDetectorStop(F_SOFTWARE_TRIGGER, static_cast<int>(block), nullptr);
}

defs::scanParameters Module::getScan() const {
    return sendToDetector<defs::scanParameters>(F_GET_SCAN);
}

void Module::setScan(const defs::scanParameters t) {
    auto retval = sendToDetector<int64_t>(F_SET_SCAN, t);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_SET_RECEIVER_SCAN, t, nullptr);
    }
    // if disabled, retval is 1, else its number of steps
    setNumberOfFrames(retval);
}

std::string Module::getScanErrorMessage() const {
    char retval[MAX_STR_LENGTH]{};
    sendToDetector(F_GET_SCAN_ERROR_MESSAGE, nullptr, retval);
    return retval;
}

// Network Configuration (Detector<->Receiver)

int Module::getNumberofUDPInterfacesFromShm() const {
    return shm()->numUDPInterfaces;
}

int Module::getNumberofUDPInterfaces() const {
    shm()->numUDPInterfaces = sendToDetector<int>(F_GET_NUM_INTERFACES);
    return shm()->numUDPInterfaces;
}

void Module::setNumberofUDPInterfaces(int n) {
    sendToDetector(F_SET_NUM_INTERFACES, n, nullptr);
    shm()->numUDPInterfaces = n;
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_SET_RECEIVER_NUM_INTERFACES, n, nullptr);
    }
}

int Module::getSelectedUDPInterface() const {
    return sendToDetector<int>(F_GET_INTERFACE_SEL);
}

void Module::selectUDPInterface(int n) {
    sendToDetector(F_SET_INTERFACE_SEL, n, nullptr);
}

sls::IpAddr Module::getSourceUDPIP() const {
    return sendToDetector<sls::IpAddr>(F_GET_SOURCE_UDP_IP);
}

void Module::setSourceUDPIP(const IpAddr ip) {
    if (ip == 0) {
        throw RuntimeError("Invalid source udp ip address");
    }
    sendToDetector(F_SET_SOURCE_UDP_IP, ip, nullptr);
}

sls::IpAddr Module::getSourceUDPIP2() const {
    return sendToDetector<sls::IpAddr>(F_GET_SOURCE_UDP_IP2);
}

void Module::setSourceUDPIP2(const IpAddr ip) {
    if (ip == 0) {
        throw RuntimeError("Invalid source udp ip address2");
    }
    sendToDetector(F_SET_SOURCE_UDP_IP2, ip, nullptr);
}

sls::MacAddr Module::getSourceUDPMAC() const {
    return sendToDetector<sls::MacAddr>(F_GET_SOURCE_UDP_MAC);
}

void Module::setSourceUDPMAC(const sls::MacAddr mac) {
    if (mac == 0) {
        throw RuntimeError("Invalid source udp mac address");
    }
    sendToDetector(F_SET_SOURCE_UDP_MAC, mac, nullptr);
}

sls::MacAddr Module::getSourceUDPMAC2() const {
    return sendToDetector<sls::MacAddr>(F_GET_SOURCE_UDP_MAC2);
}

void Module::setSourceUDPMAC2(const sls::MacAddr mac) {
    if (mac == 0) {
        throw RuntimeError("Invalid source udp mac address2");
    }
    sendToDetector(F_SET_SOURCE_UDP_MAC2, mac, nullptr);
}

sls::UdpDestination Module::getDestinationUDPList(const uint32_t entry) const {
    return sendToDetector<sls::UdpDestination>(F_GET_DEST_UDP_LIST, entry);
}

void Module::setDestinationUDPList(const sls::UdpDestination dest) {
    // set them in the default way so the receivers are also set up
    if (dest.entry == 0) {
        if (dest.port != 0) {
            setDestinationUDPPort(dest.port);
        }
        if (dest.ip != 0) {
            setDestinationUDPIP(dest.ip);
        }
        if (dest.mac != 0) {
            setDestinationUDPMAC(dest.mac);
        }
        if (dest.port2 != 0) {
            setDestinationUDPPort2(dest.port2);
        }
        if (dest.ip2 != 0) {
            setDestinationUDPIP2(dest.ip2);
        }
        if (dest.mac2 != 0) {
            setDestinationUDPMAC2(dest.mac2);
        }
    } else {
        sendToDetector(F_SET_DEST_UDP_LIST, dest, nullptr);
    }
}

int Module::getNumberofUDPDestinations() const {
    return sendToDetector<int>(F_GET_NUM_DEST_UDP);
}

void Module::clearUDPDestinations() { sendToDetector(F_CLEAR_ALL_UDP_DEST); }

int Module::getFirstUDPDestination() const {
    return sendToDetector<int>(F_GET_UDP_FIRST_DEST);
}

void Module::setFirstUDPDestination(const int value) {
    sendToDetector(F_SET_UDP_FIRST_DEST, value, nullptr);
}

sls::IpAddr Module::getDestinationUDPIP() const {
    return sendToDetector<sls::IpAddr>(F_GET_DEST_UDP_IP);
}

void Module::setDestinationUDPIP(const IpAddr ip) {
    if (ip == 0) {
        throw RuntimeError("Invalid destination udp ip address");
    }
    sendToDetector(F_SET_DEST_UDP_IP, ip, nullptr);
    if (shm()->useReceiverFlag) {
        sls::MacAddr retval(0LU);
        sendToReceiver(F_SET_RECEIVER_UDP_IP, ip, retval);
        LOG(logINFO) << "Setting destination udp mac of Module " << moduleIndex
                     << " to " << retval;
        sendToDetector(F_SET_DEST_UDP_MAC, retval, nullptr);
    }
}

sls::IpAddr Module::getDestinationUDPIP2() const {
    return sendToDetector<sls::IpAddr>(F_GET_DEST_UDP_IP2);
}

void Module::setDestinationUDPIP2(const IpAddr ip) {
    LOG(logDEBUG1) << "Setting destination udp ip2 to " << ip;
    if (ip == 0) {
        throw RuntimeError("Invalid destination udp ip address2");
    }

    sendToDetector(F_SET_DEST_UDP_IP2, ip, nullptr);
    if (shm()->useReceiverFlag) {
        sls::MacAddr retval(0LU);
        sendToReceiver(F_SET_RECEIVER_UDP_IP2, ip, retval);
        LOG(logINFO) << "Setting destination udp mac2 of Module " << moduleIndex
                     << " to " << retval;
        sendToDetector(F_SET_DEST_UDP_MAC2, retval, nullptr);
    }
}

sls::MacAddr Module::getDestinationUDPMAC() const {
    return sendToDetector<sls::MacAddr>(F_GET_DEST_UDP_MAC);
}

void Module::setDestinationUDPMAC(const MacAddr mac) {
    if (mac == 0) {
        throw RuntimeError("Invalid destination udp mac address");
    }
    sendToDetector(F_SET_DEST_UDP_MAC, mac, nullptr);
}

sls::MacAddr Module::getDestinationUDPMAC2() const {
    return sendToDetector<sls::MacAddr>(F_GET_DEST_UDP_MAC2);
}

void Module::setDestinationUDPMAC2(const MacAddr mac) {
    if (mac == 0) {
        throw RuntimeError("Invalid desinaion udp mac address2");
    }
    sendToDetector(F_SET_DEST_UDP_MAC2, mac, nullptr);
}

int Module::getDestinationUDPPort() const {
    return sendToDetector<int>(F_GET_DEST_UDP_PORT);
}

void Module::setDestinationUDPPort(const int port) {
    sendToDetector(F_SET_DEST_UDP_PORT, port, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_SET_RECEIVER_UDP_PORT, port, nullptr);
    }
}

int Module::getDestinationUDPPort2() const {
    return sendToDetector<int>(F_GET_DEST_UDP_PORT2);
}

void Module::setDestinationUDPPort2(const int port) {
    sendToDetector(F_SET_DEST_UDP_PORT2, port, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_SET_RECEIVER_UDP_PORT2, port, nullptr);
    }
}

void Module::reconfigureUDPDestination() { sendToDetector(F_RECONFIGURE_UDP); }

void Module::validateUDPConfiguration() {
    sendToDetector(F_VALIDATE_UDP_CONFIG);
}

std::string Module::printReceiverConfiguration() {
    std::ostringstream os;
    os << "\n\nModule " << moduleIndex << "\nReceiver Hostname:\t"
       << getReceiverHostname();

    if (shm()->detType == JUNGFRAU) {
        os << "\nNumber of Interfaces:\t" << getNumberofUDPInterfaces()
           << "\nSelected Interface:\t" << getSelectedUDPInterface();
    }

    os << "\nSource UDP IP:\t" << getSourceUDPIP() << "\nSource UDP MAC:\t"
       << getSourceUDPMAC() << "\nDestination UDP IP:\t"
       << getDestinationUDPIP() << "\nDestination UDP MAC:\t"
       << getDestinationUDPMAC();

    if (shm()->detType == JUNGFRAU) {
        os << "\nSource UDP IP2:\t" << getSourceUDPIP2()
           << "\nSource UDP MAC2:\t" << getSourceUDPMAC2()
           << "\nDestination UDP IP2:\t" << getDestinationUDPIP2()
           << "\nDestination UDP MAC2:\t" << getDestinationUDPMAC2();
    }
    os << "\nDestination UDP Port:\t" << getDestinationUDPPort();
    if (shm()->detType == JUNGFRAU || shm()->detType == EIGER) {
        os << "\nDestination UDP Port2:\t" << getDestinationUDPPort2();
    }
    os << "\n";
    return os.str();
}

bool Module::getTenGiga() const {
    return sendToDetector<int>(F_ENABLE_TEN_GIGA, GET_FLAG);
}

void Module::setTenGiga(bool value) {
    auto arg = static_cast<int>(value);
    auto retval = sendToDetector<int>(F_ENABLE_TEN_GIGA, arg);
    sendToDetectorStop<int>(F_ENABLE_TEN_GIGA, arg);
    arg = retval;
    if (shm()->useReceiverFlag && arg != GET_FLAG) {
        sendToReceiver<int>(F_ENABLE_RECEIVER_TEN_GIGA, arg);
    }
}

bool Module::getTenGigaFlowControl() const {
    return sendToDetector<int>(F_GET_TEN_GIGA_FLOW_CONTROL);
}

void Module::setTenGigaFlowControl(bool enable) {
    sendToDetector(F_SET_TEN_GIGA_FLOW_CONTROL, static_cast<int>(enable),
                   nullptr);
}

int Module::getTransmissionDelayFrame() const {
    return sendToDetector<int>(F_GET_TRANSMISSION_DELAY_FRAME);
}

void Module::setTransmissionDelayFrame(int value) {
    sendToDetector(F_SET_TRANSMISSION_DELAY_FRAME, value, nullptr);
}

int Module::getTransmissionDelayLeft() const {
    return sendToDetector<int>(F_GET_TRANSMISSION_DELAY_LEFT);
}

void Module::setTransmissionDelayLeft(int value) {
    sendToDetector(F_SET_TRANSMISSION_DELAY_LEFT, value, nullptr);
}

int Module::getTransmissionDelayRight() const {
    return sendToDetector<int>(F_GET_TRANSMISSION_DELAY_RIGHT);
}

void Module::setTransmissionDelayRight(int value) {
    sendToDetector(F_SET_TRANSMISSION_DELAY_RIGHT, value, nullptr);
}

// Receiver Config

bool Module::getUseReceiverFlag() const { return shm()->useReceiverFlag; }

std::string Module::getReceiverHostname() const {
    return std::string(shm()->rxHostname);
}

void Module::setReceiverHostname(const std::string &receiverIP) {
    LOG(logDEBUG1) << "Setting up Receiver with " << receiverIP;

    if (receiverIP == "none") {
        memset(shm()->rxHostname, 0, MAX_STR_LENGTH);
        sls::strcpy_safe(shm()->rxHostname, "none");
        shm()->useReceiverFlag = false;
    }

    if (getRunStatus() == RUNNING) {
        LOG(logWARNING) << "Acquisition already running, Stopping it.";
        stopAcquisition();
    }

    // start updating
    std::string host = receiverIP;
    auto res = sls::split(host, ':');
    if (res.size() > 1) {
        host = res[0];
        shm()->rxTCPPort = std::stoi(res[1]);
    }
    sls::strcpy_safe(shm()->rxHostname, host.c_str());
    shm()->useReceiverFlag = true;
    checkReceiverVersionCompatibility();

    // populate parameters from detector
    rxParameters retval;
    sendToDetector(F_GET_RECEIVER_PARAMETERS, nullptr, retval);

    // populate from shared memory
    retval.detType = shm()->detType;
    retval.numberOfModule.x = shm()->numberOfModule.x;
    retval.numberOfModule.y = shm()->numberOfModule.y;
    retval.moduleIndex = moduleIndex;
    memset(retval.hostname, 0, sizeof(retval.hostname));
    strcpy_safe(retval.hostname, shm()->hostname);

    sls::MacAddr retvals[2];
    sendToReceiver(F_SETUP_RECEIVER, retval, retvals);
    // update Modules with dest mac
    if (retval.udp_dstmac == 0 && retvals[0] != 0) {
        LOG(logINFO) << "Setting destination udp mac of "
                        "Module "
                     << moduleIndex << " to " << retvals[0];
        sendToDetector(F_SET_DEST_UDP_MAC, retvals[0], nullptr);
    }
    if (retval.udp_dstmac2 == 0 && retvals[1] != 0) {
        LOG(logINFO) << "Setting destination udp mac2 of "
                        "Module "
                     << moduleIndex << " to " << retvals[1];
        sendToDetector(F_SET_DEST_UDP_MAC2, retvals[1], nullptr);
    }

    shm()->numUDPInterfaces = retval.udpInterfaces;

    // to use rx_hostname if empty and also update client zmqip
    updateReceiverStreamingIP();
}

int Module::getReceiverPort() const { return shm()->rxTCPPort; }

int Module::setReceiverPort(int port_number) {
    if (port_number >= 0 && port_number != shm()->rxTCPPort) {
        if (shm()->useReceiverFlag) {
            shm()->rxTCPPort =
                sendToReceiver<int>(F_SET_RECEIVER_PORT, port_number);
        } else {
            shm()->rxTCPPort = port_number;
        }
    }
    return shm()->rxTCPPort;
}

int Module::getReceiverFifoDepth() const {
    return sendToReceiver<int>(F_SET_RECEIVER_FIFO_DEPTH, GET_FLAG);
}

void Module::setReceiverFifoDepth(int n_frames) {
    sendToReceiver<int>(F_SET_RECEIVER_FIFO_DEPTH, n_frames);
}

bool Module::getReceiverSilentMode() const {
    return sendToReceiver<int>(F_GET_RECEIVER_SILENT_MODE);
}

void Module::setReceiverSilentMode(bool enable) {
    sendToReceiver(F_SET_RECEIVER_SILENT_MODE, static_cast<int>(enable),
                   nullptr);
}

slsDetectorDefs::frameDiscardPolicy
Module::getReceiverFramesDiscardPolicy() const {
    return sendToReceiver<frameDiscardPolicy>(F_GET_RECEIVER_DISCARD_POLICY);
}

void Module::setReceiverFramesDiscardPolicy(frameDiscardPolicy f) {
    sendToReceiver(F_SET_RECEIVER_DISCARD_POLICY, static_cast<int>(f), nullptr);
}

bool Module::getPartialFramesPadding() const {
    return sendToReceiver<int>(F_GET_RECEIVER_PADDING);
}

void Module::setPartialFramesPadding(bool padding) {
    sendToReceiver(F_SET_RECEIVER_PADDING, static_cast<int>(padding), nullptr);
}

int Module::getReceiverUDPSocketBufferSize() const {
    int arg = GET_FLAG;
    return sendToReceiver<int>(F_RECEIVER_UDP_SOCK_BUF_SIZE, arg);
}

int Module::getReceiverRealUDPSocketBufferSize() const {
    return sendToReceiver<int>(F_RECEIVER_REAL_UDP_SOCK_BUF_SIZE);
}

void Module::setReceiverUDPSocketBufferSize(int udpsockbufsize) {
    sendToReceiver<int>(F_RECEIVER_UDP_SOCK_BUF_SIZE, udpsockbufsize);
}

bool Module::getReceiverLock() const {
    return sendToReceiver<int>(F_LOCK_RECEIVER, GET_FLAG);
}

void Module::setReceiverLock(bool lock) {
    sendToReceiver<int>(F_LOCK_RECEIVER, static_cast<int>(lock));
}

sls::IpAddr Module::getReceiverLastClientIP() const {
    return sendToReceiver<sls::IpAddr>(F_GET_LAST_RECEIVER_CLIENT_IP);
}

std::array<pid_t, NUM_RX_THREAD_IDS> Module::getReceiverThreadIds() const {
    return sendToReceiver<std::array<pid_t, NUM_RX_THREAD_IDS>>(
        F_GET_RECEIVER_THREAD_IDS);
}

// File
slsDetectorDefs::fileFormat Module::getFileFormat() const {
    return sendToReceiver<fileFormat>(F_GET_RECEIVER_FILE_FORMAT);
}

void Module::setFileFormat(fileFormat f) {
    sendToReceiver(F_SET_RECEIVER_FILE_FORMAT, f, nullptr);
}

std::string Module::getFilePath() const {
    char ret[MAX_STR_LENGTH]{};
    sendToReceiver(F_GET_RECEIVER_FILE_PATH, nullptr, ret);
    return ret;
}

void Module::setFilePath(const std::string &path) {
    if (path.empty()) {
        throw RuntimeError("Cannot set empty file path");
    }
    char args[MAX_STR_LENGTH]{};
    sls::strcpy_safe(args, path.c_str());
    sendToReceiver(F_SET_RECEIVER_FILE_PATH, args, nullptr);
}

std::string Module::getFileName() const {
    char buff[MAX_STR_LENGTH]{};
    sendToReceiver(F_GET_RECEIVER_FILE_NAME, nullptr, buff);
    return buff;
}

void Module::setFileName(const std::string &fname) {
    if (fname.empty()) {
        throw RuntimeError("Cannot set empty file name prefix");
    }
    char args[MAX_STR_LENGTH]{};
    sls::strcpy_safe(args, fname.c_str());
    sendToReceiver(F_SET_RECEIVER_FILE_NAME, args, nullptr);
}

int64_t Module::getFileIndex() const {
    return sendToReceiver<int64_t>(F_GET_RECEIVER_FILE_INDEX);
}

void Module::setFileIndex(int64_t file_index) {
    sendToReceiver(F_SET_RECEIVER_FILE_INDEX, file_index, nullptr);
}

void Module::incrementFileIndex() { sendToReceiver(F_INCREMENT_FILE_INDEX); }

bool Module::getFileWrite() const {
    return sendToReceiver<int>(F_GET_RECEIVER_FILE_WRITE);
}

void Module::setFileWrite(bool value) {
    sendToReceiver(F_SET_RECEIVER_FILE_WRITE, static_cast<int>(value), nullptr);
}

bool Module::getMasterFileWrite() const {
    return sendToReceiver<int>(F_GET_RECEIVER_MASTER_FILE_WRITE);
}

void Module::setMasterFileWrite(bool value) {
    sendToReceiver(F_SET_RECEIVER_MASTER_FILE_WRITE, static_cast<int>(value),
                   nullptr);
}

bool Module::getFileOverWrite() const {
    return sendToReceiver<int>(F_GET_RECEIVER_OVERWRITE);
}

void Module::setFileOverWrite(bool value) {
    sendToReceiver(F_SET_RECEIVER_OVERWRITE, static_cast<int>(value), nullptr);
}

int Module::getFramesPerFile() const {
    return sendToReceiver<int>(F_GET_RECEIVER_FRAMES_PER_FILE);
}

void Module::setFramesPerFile(int n_frames) {
    sendToReceiver(F_SET_RECEIVER_FRAMES_PER_FILE, n_frames, nullptr);
}

// ZMQ Streaming Parameters (Receiver<->Client)

bool Module::getReceiverStreaming() const {
    return sendToReceiver<int>(F_GET_RECEIVER_STREAMING);
}

void Module::setReceiverStreaming(bool enable) {
    sendToReceiver(F_SET_RECEIVER_STREAMING, static_cast<int>(enable), nullptr);
}

int Module::getReceiverStreamingFrequency() const {
    return sendToReceiver<int>(F_GET_RECEIVER_STREAMING_FREQUENCY);
}

void Module::setReceiverStreamingFrequency(int freq) {
    if (freq < 0) {
        throw RuntimeError("Invalid streaming frequency " +
                           std::to_string(freq));
    }
    sendToReceiver(F_SET_RECEIVER_STREAMING_FREQUENCY, freq, nullptr);
}

int Module::getReceiverStreamingTimer() const {
    return sendToReceiver<int>(F_RECEIVER_STREAMING_TIMER, GET_FLAG);
}

void Module::setReceiverStreamingTimer(int time_in_ms) {
    sendToReceiver<int>(F_RECEIVER_STREAMING_TIMER, time_in_ms);
}

int Module::getReceiverStreamingStartingFrame() const {
    return sendToReceiver<int>(F_GET_RECEIVER_STREAMING_START_FNUM);
}

void Module::setReceiverStreamingStartingFrame(int fnum) {
    if (fnum < 0) {
        throw RuntimeError("Invalid streaming starting frame number " +
                           std::to_string(fnum));
    }
    sendToReceiver(F_SET_RECEIVER_STREAMING_START_FNUM, fnum, nullptr);
}

int Module::getReceiverStreamingPort() const {
    return sendToReceiver<int>(F_GET_RECEIVER_STREAMING_PORT);
}

void Module::setReceiverStreamingPort(int port) {
    sendToReceiver(F_SET_RECEIVER_STREAMING_PORT, port, nullptr);
}

sls::IpAddr Module::getReceiverStreamingIP() const {
    return sendToReceiver<sls::IpAddr>(F_GET_RECEIVER_STREAMING_SRC_IP);
}

void Module::setReceiverStreamingIP(const sls::IpAddr ip) {
    if (ip == 0) {
        throw RuntimeError("Invalid receiver zmq ip address");
    }
    // if client zmqip is empty, update it
    if (shm()->zmqip == 0) {
        shm()->zmqip = ip;
    }
    sendToReceiver(F_SET_RECEIVER_STREAMING_SRC_IP, ip, nullptr);
}

int Module::getClientStreamingPort() const { return shm()->zmqport; }

void Module::setClientStreamingPort(int port) { shm()->zmqport = port; }

sls::IpAddr Module::getClientStreamingIP() const { return shm()->zmqip; }

void Module::setClientStreamingIP(const sls::IpAddr ip) {
    if (ip == 0) {
        throw RuntimeError("Invalid client zmq ip address");
    }
    shm()->zmqip = ip;
}

int Module::getReceiverStreamingHwm() const {
    return sendToReceiver<int>(F_GET_RECEIVER_STREAMING_HWM);
}

void Module::setReceiverStreamingHwm(const int limit) {
    sendToReceiver(F_SET_RECEIVER_STREAMING_HWM, limit, nullptr);
}

//  Eiger Specific

int64_t Module::getSubExptime() const {
    return sendToDetector<int64_t>(F_GET_SUB_EXPTIME);
}

void Module::setSubExptime(int64_t value) {
    int64_t prevVal = value;
    if (shm()->detType == EIGER) {
        prevVal = getSubExptime();
    }
    sendToDetector(F_SET_SUB_EXPTIME, value, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_RECEIVER_SET_SUB_EXPTIME, value, nullptr);
    }
    if (prevVal != value) {
        updateRateCorrection();
    }
}

int64_t Module::getSubDeadTime() const {
    return sendToDetector<int64_t>(F_GET_SUB_DEADTIME);
}

void Module::setSubDeadTime(int64_t value) {
    sendToDetector(F_SET_SUB_DEADTIME, value, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_RECEIVER_SET_SUB_DEADTIME, value, nullptr);
    }
}

bool Module::getOverFlowMode() const {
    return sendToDetector<int>(F_GET_OVERFLOW_MODE);
}

void Module::setOverFlowMode(const bool enable) {
    sendToDetector(F_SET_OVERFLOW_MODE, static_cast<int>(enable), nullptr);
}

int64_t Module::getRateCorrection() const {
    return sendToDetector<int64_t>(F_GET_RATE_CORRECT);
}

void Module::setDefaultRateCorrection() {
    int64_t arg = -1;
    sendToDetector(F_SET_RATE_CORRECT, arg, nullptr);
}

void Module::setRateCorrection(int64_t t) {
    sendToDetector(F_SET_RATE_CORRECT, t, nullptr);
}

void Module::sendReceiverRateCorrections(const std::vector<int64_t> &t) {
    LOG(logDEBUG) << "Sending to receiver 0 [rate corrections: " << ToString(t)
                  << ']';
    auto receiver = ReceiverSocket(shm()->rxHostname, shm()->rxTCPPort);
    receiver.Send(F_SET_RECEIVER_RATE_CORRECT);
    receiver.Send(static_cast<int>(t.size()));
    receiver.Send(t);
    if (receiver.Receive<int>() == FAIL) {
        throw RuntimeError("Receiver " + std::to_string(moduleIndex) +
                           " returned error: " + receiver.readErrorMessage());
    }
}

bool Module::getInterruptSubframe() const {
    return sendToDetector<int>(F_GET_INTERRUPT_SUBFRAME);
}

void Module::setInterruptSubframe(const bool enable) {
    sendToDetector(F_SET_INTERRUPT_SUBFRAME, static_cast<int>(enable), nullptr);
}

int64_t Module::getMeasuredPeriod() const {
    return sendToDetectorStop<int64_t>(F_GET_MEASURED_PERIOD);
}

int64_t Module::getMeasuredSubFramePeriod() const {
    return sendToDetectorStop<int64_t>(F_GET_MEASURED_SUBPERIOD);
}

bool Module::getActivate() const {
    auto retval = sendToDetector<int>(F_ACTIVATE, GET_FLAG);
    auto retval2 = sendToDetectorStop<int>(F_ACTIVATE, GET_FLAG);
    if (retval != retval2) {
        std::ostringstream oss;
        oss << "Inconsistent activate state. Control Server: " << retval
            << ". Stop Server: " << retval2;
        throw RuntimeError(oss.str());
    }
    return retval;
}

void Module::setActivate(const bool enable) {
    int arg = static_cast<int>(enable);
    auto retval = sendToDetector<int>(F_ACTIVATE, arg);
    sendToDetectorStop<int>(F_ACTIVATE, arg);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_RECEIVER_ACTIVATE, retval, nullptr);
    }
}

bool Module::getDeactivatedRxrPaddingMode() const {
    return sendToReceiver<int>(F_GET_RECEIVER_DEACTIVATED_PADDING);
}

void Module::setDeactivatedRxrPaddingMode(bool padding) {
    sendToReceiver(F_SET_RECEIVER_DEACTIVATED_PADDING,
                   static_cast<int>(padding), nullptr);
}

bool Module::getCounterBit() const {
    return (
        !static_cast<bool>(sendToDetector<int>(F_SET_COUNTER_BIT, GET_FLAG)));
}

void Module::setCounterBit(bool cb) {
    sendToDetector<int>(F_SET_COUNTER_BIT, static_cast<int>(!cb));
}

void Module::pulsePixel(int n, int x, int y) {
    const int args[]{n, x, y};
    sendToDetector(F_PULSE_PIXEL, args, nullptr);
}

void Module::pulsePixelNMove(int n, int x, int y) {
    const int args[]{n, x, y};
    sendToDetector(F_PULSE_PIXEL_AND_MOVE, args, nullptr);
}

void Module::pulseChip(int n_pulses) {
    sendToDetector(F_PULSE_CHIP, n_pulses, nullptr);
}

bool Module::getQuad() const { return sendToDetector<int>(F_GET_QUAD) != 0; }

void Module::setQuad(const bool enable) {
    int value = enable ? 1 : 0;
    sendToDetector(F_SET_QUAD, value, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_SET_RECEIVER_QUAD, value, nullptr);
    }
}

bool Module::getDataStream(const portPosition port) const {
    return sendToDetector<int>(F_GET_DATASTREAM, static_cast<int>(port));
}

void Module::setDataStream(const portPosition port, const bool enable) {
    int args[]{static_cast<int>(port), static_cast<int>(enable)};
    sendToDetector(F_SET_DATASTREAM, args, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_RECEIVER_SET_DATASTREAM, args, nullptr);
    }
}

// Jungfrau Specific
double Module::getChipVersion() const {
    return (sendToDetector<int>(F_GET_CHIP_VERSION)) / 10.00;
}

int Module::getThresholdTemperature() const {
    auto retval = sendToDetectorStop<int>(F_THRESHOLD_TEMP, GET_FLAG);
    return retval / 1000;
}

void Module::setThresholdTemperature(int val) {
    if (val <= 0) {
        throw RuntimeError("Invalid threshold temperature " +
                           std::to_string(val));
    }
    val *= 1000;
    sendToDetectorStop<int>(F_THRESHOLD_TEMP, val);
}

bool Module::getTemperatureControl() const {
    return sendToDetectorStop<int>(F_TEMP_CONTROL, GET_FLAG);
}

void Module::setTemperatureControl(bool val) {
    sendToDetectorStop<int>(F_TEMP_CONTROL, static_cast<int>(val));
}

int Module::getTemperatureEvent() const {
    return sendToDetectorStop<int>(F_TEMP_EVENT, GET_FLAG);
}

void Module::resetTemperatureEvent() {
    sendToDetectorStop<int>(F_TEMP_EVENT, 0);
}

bool Module::getAutoComparatorDisableMode() const {
    return sendToDetector<int>(F_AUTO_COMP_DISABLE, GET_FLAG);
}

void Module::setAutoComparatorDisableMode(bool val) {
    sendToDetector<int>(F_AUTO_COMP_DISABLE, static_cast<int>(val));
}

int64_t Module::getComparatorDisableTime() const {
    return sendToDetector<int64_t>(F_GET_COMP_DISABLE_TIME);
}

void Module::setComparatorDisableTime(int64_t value) {
    sendToDetector(F_SET_COMP_DISABLE_TIME, value, nullptr);
}

int Module::getNumberOfAdditionalStorageCells() const {
    return sendToDetector<int>(F_GET_NUM_ADDITIONAL_STORAGE_CELLS);
}

void Module::setNumberOfAdditionalStorageCells(int value) {
    sendToDetector(F_SET_NUM_ADDITIONAL_STORAGE_CELLS, value, nullptr);
}

int Module::getStorageCellStart() const {
    return sendToDetector<int>(F_STORAGE_CELL_START, GET_FLAG);
}

void Module::setStorageCellStart(int pos) {
    sendToDetector<int>(F_STORAGE_CELL_START, pos);
}

int64_t Module::getStorageCellDelay() const {
    return sendToDetector<int64_t>(F_GET_STORAGE_CELL_DELAY);
}

void Module::setStorageCellDelay(int64_t value) {
    sendToDetector(F_SET_STORAGE_CELL_DELAY, value, nullptr);
}

slsDetectorDefs::gainMode Module::getGainMode() const {
    return sendToDetector<gainMode>(F_GET_GAIN_MODE);
}

void Module::setGainMode(const slsDetectorDefs::gainMode mode) {
    sendToDetector(F_SET_GAIN_MODE, mode, nullptr);
}

int Module::getFilterCell() const {
    return sendToDetector<int>(F_GET_FILTER_CELL);
}

void Module::setFilterCell(int value) {
    sendToDetector(F_SET_FILTER_CELL, value, nullptr);
}

// Gotthard Specific

slsDetectorDefs::ROI Module::getROI() const {
    return sendToDetector<slsDetectorDefs::ROI>(F_GET_ROI);
}

void Module::setROI(slsDetectorDefs::ROI arg) {
    if (arg.xmin < 0 || arg.xmax >= getNumberOfChannels().x) {
        arg.xmin = -1;
        arg.xmax = -1;
    }
    sendToDetector(F_SET_ROI, arg, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_RECEIVER_SET_ROI, arg, nullptr);
    }
}

void Module::clearROI() { setROI(slsDetectorDefs::ROI{}); }

int64_t Module::getExptimeLeft() const {
    return sendToDetectorStop<int64_t>(F_GET_EXPTIME_LEFT);
}

// Gotthard2 Specific

int64_t Module::getNumberOfBursts() const {
    return sendToDetector<int64_t>(F_GET_NUM_BURSTS);
}

void Module::setNumberOfBursts(int64_t value) {
    sendToDetector(F_SET_NUM_BURSTS, value, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_SET_RECEIVER_NUM_BURSTS, value, nullptr);
    }
}

int64_t Module::getBurstPeriod() const {
    return sendToDetector<int64_t>(F_GET_BURST_PERIOD);
}

void Module::setBurstPeriod(int64_t value) {
    sendToDetector(F_SET_BURST_PERIOD, value, nullptr);
}

int64_t Module::getNumberOfBurstsLeft() const {
    return sendToDetectorStop<int64_t>(F_GET_BURSTS_LEFT);
}

std::array<int, 2> Module::getInjectChannel() const {
    return sendToDetector<std::array<int, 2>>(F_GET_INJECT_CHANNEL);
}

void Module::setInjectChannel(const int offsetChannel,
                              const int incrementChannel) {
    int args[]{offsetChannel, incrementChannel};
    sendToDetector(F_SET_INJECT_CHANNEL, args, nullptr);
}

void Module::sendVetoPhoton(const int chipIndex,
                            const std::vector<int> &gainIndices,
                            const std::vector<int> &values) {
    const int nch = gainIndices.size();
    if (gainIndices.size() != values.size()) {
        throw RuntimeError("Number of Gain Indices and values do not match! "
                           "Gain Indices size: " +
                           std::to_string(gainIndices.size()) +
                           ", values size: " + std::to_string(values.size()));
    }
    LOG(logDEBUG1) << "Sending veto photon/file to detector [chip:" << chipIndex
                   << ", nch:" << nch << "]";

    const int args[]{chipIndex, nch};
    auto client = DetectorSocket(shm()->hostname, shm()->controlPort);
    client.Send(F_SET_VETO_PHOTON);
    client.Send(args);
    client.Send(gainIndices);
    client.Send(values);
    if (client.Receive<int>() == FAIL) {
        throw RuntimeError("Detector " + std::to_string(moduleIndex) +
                           " returned error: " + client.readErrorMessage());
    }
}

void Module::getVetoPhoton(const int chipIndex,
                           const std::string &fname) const {
    LOG(logDEBUG1) << "Getting veto photon [" << chipIndex << "]\n";
    auto client = DetectorSocket(shm()->hostname, shm()->controlPort);
    client.Send(F_GET_VETO_PHOTON);
    client.Send(chipIndex);
    if (client.Receive<int>() == FAIL) {
        throw RuntimeError("Detector " + std::to_string(moduleIndex) +
                           " returned error: " + client.readErrorMessage());
    }

    auto nch = client.Receive<int>();
    if (nch != shm()->nChan.x) {
        throw RuntimeError("Could not get veto photon. Expected " +
                           std::to_string(shm()->nChan.x) + " channels, got " +
                           std::to_string(nch));
    }
    std::vector<int> gainIndices(nch);
    std::vector<int> values(nch);
    client.Receive(gainIndices);
    client.Receive(values);

    // save to file
    std::ofstream outfile(fname);
    if (!outfile) {
        throw RuntimeError("Could not create file to save veto photon");
    }
    for (int i = 0; i < nch; ++i) {
        outfile << gainIndices[i] << ' ' << values[i] << '\n';
    }
    LOG(logDEBUG1) << nch << " veto photon saved to file";
}

void Module::setVetoPhoton(const int chipIndex, const int numPhotons,
                           const int energy, const std::string &fname) {
    if (shm()->detType != GOTTHARD2) {
        throw RuntimeError(
            "Set Veto reference is not implemented for this detector");
    }
    if (chipIndex < -1 || chipIndex >= shm()->nChip.x) {
        throw RuntimeError("Could not set veto photon. Invalid chip index: " +
                           std::to_string(chipIndex));
    }
    if (numPhotons < 1) {
        throw RuntimeError(
            "Could not set veto photon. Invalid number of photons: " +
            std::to_string(numPhotons));
    }
    if (energy < 1) {
        throw RuntimeError("Could not set veto photon. Invalid energy: " +
                           std::to_string(energy));
    }
    std::ifstream infile(fname.c_str());
    if (!infile) {
        throw RuntimeError("Could not set veto photon. Could not open file: " +
                           fname);
    }
    LOG(logDEBUG1) << "Setting veto photon. Reading Gain values from file";

    const int totalEnergy = numPhotons * energy;
    std::vector<int> gainIndices;
    std::vector<int> values;

    while (infile.good()) {
        std::string line;
        getline(infile, line);
        if (line.find('#') != std::string::npos) {
            line.erase(line.find('#'));
        }
        if (line.length() < 1) {
            continue;
        }
        std::istringstream ss(line);

        // read pedestal, gain values, gain thresholds
        double gainPedestal[3] = {-1, -1, -1};
        double gainValue[3] = {-1, -1, -1};
        int gainThreshold[2] = {-1, -1};
        ss >> gainPedestal[0] >> gainPedestal[1] >> gainPedestal[2] >>
            gainValue[0] >> gainValue[1] >> gainValue[2] >> gainThreshold[0] >>
            gainThreshold[1];
        if (ss.fail()) {
            throw RuntimeError("Could not set veto photon. Invalid pedestals, "
                               "gain values or gain thresholds for channel " +
                               std::to_string(gainIndices.size()));
        }

        // caluclate gain index from gain thresholds and threshold energy
        int gainIndex = 2;
        if (totalEnergy < gainThreshold[0]) {
            gainIndex = 0;
        } else if (totalEnergy < gainThreshold[1]) {
            gainIndex = 1;
        }
        gainIndices.push_back(gainIndex);
        // calculate ADU value
        values.push_back(gainPedestal[gainIndex] +
                         (gainValue[gainIndex] * totalEnergy));
    }
    // check size
    if ((int)gainIndices.size() != shm()->nChan.x) {
        throw RuntimeError("Could not set veto photon. Invalid number of "
                           "entries in file. Expected " +
                           std::to_string(shm()->nChan.x) + ", read " +
                           std::to_string(gainIndices.size()));
    }

    sendVetoPhoton(chipIndex, gainIndices, values);
}

void Module::setVetoReference(const int gainIndex, const int value) {
    const int args[]{gainIndex, value};
    sendToDetector(F_SET_VETO_REFERENCE, args, nullptr);
}

void Module::setVetoFile(const int chipIndex, const std::string &fname) {
    if (shm()->detType != GOTTHARD2) {
        throw RuntimeError(
            "Set Veto file is not implemented for this detector");
    }
    if (chipIndex < -1 || chipIndex >= shm()->nChip.x) {
        throw RuntimeError("Could not set veto file. Invalid chip index: " +
                           std::to_string(chipIndex));
    }

    std::ifstream input_file(fname);
    if (!input_file) {
        throw RuntimeError("Could not set veto file for chip " +
                           std::to_string(chipIndex) +
                           ". Could not open file: " + fname);
    }

    std::vector<int> gainIndices;
    std::vector<int> values;

    for (std::string line; std::getline(input_file, line);) {
        if (line.find('#') != std::string::npos) {
            line.erase(line.find('#'));
        }
        LOG(logDEBUG1) << "line after removing comments:\n\t" << line;
        if (line.length() > 1) {
            // convert command and string to a vector
            std::istringstream iss(line);
            std::string val;
            int gainIndex = -1;
            int value = -1;
            iss >> gainIndex >> val;
            if (iss.fail()) {
                throw RuntimeError("Could not set veto file. Invalid gain "
                                   "or reference value for channel " +
                                   std::to_string(gainIndices.size()));
            }
            try {
                value = StringTo<int>(val);
            } catch (...) {
                throw RuntimeError("Could not set veto file. Invalid value " +
                                   val + " for channel " +
                                   std::to_string(gainIndices.size()));
            }
            gainIndices.push_back(gainIndex);
            values.push_back(value);
        }
    }
    // check size
    if ((int)gainIndices.size() != shm()->nChan.x) {
        throw RuntimeError("Could not set veto file. Invalid number of "
                           "entries in file. Expected " +
                           std::to_string(shm()->nChan.x) + ", read " +
                           std::to_string(gainIndices.size()));
    }

    sendVetoPhoton(chipIndex, gainIndices, values);
}

slsDetectorDefs::burstMode Module::getBurstMode() const {
    return sendToDetector<burstMode>(F_GET_BURST_MODE);
}

void Module::setBurstMode(slsDetectorDefs::burstMode value) {
    sendToDetector(F_SET_BURST_MODE, value, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_SET_RECEIVER_BURST_MODE, value, nullptr);
    }
}

bool Module::getCDSGain() const { return sendToDetector<int>(F_GET_CDS_GAIN); }

void Module::setCDSGain(bool value) {
    sendToDetector(F_SET_CDS_GAIN, static_cast<int>(value), nullptr);
}

slsDetectorDefs::timingSourceType Module::getTimingSource() const {
    return sendToDetector<timingSourceType>(F_GET_TIMING_SOURCE);
}

void Module::setTimingSource(slsDetectorDefs::timingSourceType value) {
    sendToDetector(F_SET_TIMING_SOURCE, static_cast<int>(value), nullptr);
}

bool Module::getVeto() const { return sendToDetector<int>(F_GET_VETO); }

void Module::setVeto(bool enable) {
    sendToDetector(F_SET_VETO, static_cast<int>(enable), nullptr);
}

bool Module::getVetoStream() const {
    return (sendToDetector<int>(F_GET_VETO_STREAM));
}

void Module::setVetoStream(const bool value) {
    sendToDetector(F_SET_VETO_STREAM, static_cast<int>(value), nullptr);
}

slsDetectorDefs::vetoAlgorithm Module::getVetoAlgorithm(
    const slsDetectorDefs::streamingInterface interface) const {
    return sendToDetector<vetoAlgorithm>(F_GET_VETO_ALGORITHM,
                                         static_cast<int>(interface));
}

void Module::setVetoAlgorithm(
    const slsDetectorDefs::vetoAlgorithm alg,
    const slsDetectorDefs::streamingInterface interface) {
    int args[]{static_cast<int>(alg), static_cast<int>(interface)};
    sendToDetector(F_SET_VETO_ALGORITHM, args, nullptr);
}

int Module::getADCConfiguration(const int chipIndex, const int adcIndex) const {
    int args[]{chipIndex, adcIndex};
    return sendToDetector<int>(F_GET_ADC_CONFIGURATION, args);
}

void Module::setADCConfiguration(const int chipIndex, const int adcIndex,
                                 int value) {
    int args[]{chipIndex, adcIndex, value};
    sendToDetector(F_SET_ADC_CONFIGURATION, args, nullptr);
}

void Module::getBadChannels(const std::string &fname) const {
    LOG(logDEBUG1) << "Getting bad channels to " << fname;
    auto client = DetectorSocket(shm()->hostname, shm()->controlPort);
    client.Send(F_GET_BAD_CHANNELS);
    if (client.Receive<int>() == FAIL) {
        throw RuntimeError("Detector " + std::to_string(moduleIndex) +
                           " returned error: " + client.readErrorMessage());
    }
    // receive badchannels
    auto nch = client.Receive<int>();
    std::vector<int> badchannels(nch);
    if (nch > 0) {
        client.Receive(badchannels);
        for (size_t i = 0; i < badchannels.size(); ++i) {
            LOG(logDEBUG1) << i << ":" << badchannels[i];
        }
    }

    // save to file
    std::ofstream outfile(fname);
    if (!outfile) {
        throw RuntimeError("Could not create file to save bad channels");
    }
    for (auto ch : badchannels)
        outfile << ch << '\n';
    LOG(logDEBUG1) << nch << " bad channels saved to file";
}

void Module::setBadChannels(const std::string &fname) {
    // read bad channels file
    std::ifstream input_file(fname);
    if (!input_file) {
        throw RuntimeError("Could not open bad channels file " + fname +
                           " for reading");
    }
    std::vector<int> badchannels;
    for (std::string line; std::getline(input_file, line);) {
        line.erase(std::remove_if(begin(line), end(line), isspace),
                   end(line)); // remove space
        if (!line.empty()) {
            std::istringstream iss(line);
            int ival = 0;
            iss >> ival;
            if (iss.fail()) {
                throw RuntimeError("Could not load bad channels file. Invalid "
                                   "channel number at position " +
                                   std::to_string(badchannels.size()));
            }
            badchannels.push_back(ival);
        }
    }

    // send bad channels to module
    auto nch = static_cast<int>(badchannels.size());
    LOG(logDEBUG1) << "Sending bad channels to detector, nch:" << nch;
    auto client = DetectorSocket(shm()->hostname, shm()->controlPort);
    client.Send(F_SET_BAD_CHANNELS);
    client.Send(nch);
    if (nch > 0) {
        client.Send(badchannels);
    }
    if (client.Receive<int>() == FAIL) {
        throw RuntimeError("Detector " + std::to_string(moduleIndex) +
                           " returned error: " + client.readErrorMessage());
    }
}

// Mythen3 Specific

uint32_t Module::getCounterMask() const {
    return sendToDetector<uint32_t>(F_GET_COUNTER_MASK);
}

void Module::setCounterMask(uint32_t countermask) {
    LOG(logDEBUG1) << "Setting Counter mask to " << countermask;
    sendToDetector(F_SET_COUNTER_MASK, countermask, nullptr);
    if (shm()->useReceiverFlag) {
        LOG(logDEBUG1) << "Sending Reciver counter mask: " << countermask;
        sendToReceiver(F_RECEIVER_SET_COUNTER_MASK, countermask, nullptr);
    }
}

int Module::getNumberOfGates() const {
    return sendToDetector<int>(F_GET_NUM_GATES);
}

void Module::setNumberOfGates(int value) {
    sendToDetector(F_SET_NUM_GATES, value, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_SET_RECEIVER_NUM_GATES, value, nullptr);
    }
}

std::array<time::ns, 3> Module::getExptimeForAllGates() const {
    return sendToDetector<std::array<time::ns, 3>>(F_GET_EXPTIME_ALL_GATES);
}

int64_t Module::getGateDelay(int gateIndex) const {
    return sendToDetector<int64_t>(F_GET_GATE_DELAY, gateIndex);
}

void Module::setGateDelay(int gateIndex, int64_t value) {
    int64_t args[]{static_cast<int64_t>(gateIndex), value};
    sendToDetector(F_SET_GATE_DELAY, args, nullptr);
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_SET_RECEIVER_GATE_DELAY, args, nullptr);
    }
}

std::array<time::ns, 3> Module::getGateDelayForAllGates() const {
    return sendToDetector<std::array<time::ns, 3>>(F_GET_GATE_DELAY_ALL_GATES);
}

bool Module::isMaster() const { return sendToDetectorStop<int>(F_GET_MASTER); }

int Module::getChipStatusRegister() const {
    return sendToDetector<int>(F_GET_CSR);
}

void Module::setGainCaps(int caps) {
    sendToDetector<int>(F_SET_GAIN_CAPS, caps);
}

int Module::getGainCaps() { return sendToDetector<int>(F_GET_GAIN_CAPS); }

// CTB / Moench Specific
int Module::getNumberOfAnalogSamples() const {
    return sendToDetector<int>(F_GET_NUM_ANALOG_SAMPLES);
}

void Module::setNumberOfAnalogSamples(int value) {
    sendToDetector(F_SET_NUM_ANALOG_SAMPLES, value, nullptr);
    // update #nchan, as it depends on #samples, adcmask
    updateNumberOfChannels();
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_RECEIVER_SET_NUM_ANALOG_SAMPLES, value, nullptr);
    }
}

int Module::getADCPipeline() const {
    return sendToDetector<int>(F_GET_ADC_PIPELINE);
}

void Module::setADCPipeline(int value) {
    sendToDetector(F_SET_ADC_PIPELINE, value, nullptr);
}

uint32_t Module::getADCEnableMask() const {
    return sendToDetector<uint32_t>(F_GET_ADC_ENABLE_MASK);
}

void Module::setADCEnableMask(uint32_t mask) {
    sendToDetector(F_SET_ADC_ENABLE_MASK, mask, nullptr);
    // update #nchan, as it depends on #samples, adcmask,
    updateNumberOfChannels();

    if (shm()->useReceiverFlag) {
        sendToReceiver<int>(F_RECEIVER_SET_ADC_MASK, mask);
    }
}

uint32_t Module::getTenGigaADCEnableMask() const {
    return sendToDetector<uint32_t>(F_GET_ADC_ENABLE_MASK_10G);
}

void Module::setTenGigaADCEnableMask(uint32_t mask) {
    sendToDetector(F_SET_ADC_ENABLE_MASK_10G, mask, nullptr);
    updateNumberOfChannels(); // depends on samples and adcmask

    if (shm()->useReceiverFlag) {
        sendToReceiver<int>(F_RECEIVER_SET_ADC_MASK_10G, mask);
    }
}

// CTB Specific

int Module::getNumberOfDigitalSamples() const {
    return sendToDetector<int>(F_GET_NUM_DIGITAL_SAMPLES);
}

void Module::setNumberOfDigitalSamples(int value) {
    LOG(logDEBUG1) << "Setting number of digital samples to " << value;
    sendToDetector(F_SET_NUM_DIGITAL_SAMPLES, value, nullptr);
    updateNumberOfChannels(); // depends on samples and adcmask
    if (shm()->useReceiverFlag) {
        LOG(logDEBUG1) << "Sending number of digital samples to Receiver: "
                       << value;
        sendToReceiver(F_RECEIVER_SET_NUM_DIGITAL_SAMPLES, value, nullptr);
    }
}

slsDetectorDefs::readoutMode Module::getReadoutMode() const {
    return sendToDetector<readoutMode>(F_GET_READOUT_MODE);
}

void Module::setReadoutMode(const slsDetectorDefs::readoutMode mode) {
    auto arg = static_cast<uint32_t>(mode); // TODO! unit?
    sendToDetector(F_SET_READOUT_MODE, arg, nullptr);
    // update #nchan, as it depends on #samples, adcmask,
    if (shm()->detType == CHIPTESTBOARD) {
        updateNumberOfChannels();
    }
    if (shm()->useReceiverFlag) {
        sendToReceiver(F_RECEIVER_SET_READOUT_MODE, mode, nullptr);
    }
}

int Module::getExternalSamplingSource() {
    return setExternalSamplingSource(GET_FLAG);
}

int Module::setExternalSamplingSource(int value) {
    return sendToDetector<int>(F_EXTERNAL_SAMPLING_SOURCE, value);
}

bool Module::getExternalSampling() const {
    return sendToDetector<int>(F_EXTERNAL_SAMPLING, GET_FLAG);
}

void Module::setExternalSampling(bool value) {
    sendToDetector<int>(F_EXTERNAL_SAMPLING, static_cast<int>(value));
}

std::vector<int> Module::getReceiverDbitList() const {
    return sendToReceiver<sls::StaticVector<int, MAX_RX_DBIT>>(
        F_GET_RECEIVER_DBIT_LIST);
}

void Module::setReceiverDbitList(std::vector<int> list) {
    LOG(logDEBUG1) << "Setting Receiver Dbit List";
    if (list.size() > 64) {
        throw sls::RuntimeError("Dbit list size cannot be greater than 64\n");
    }
    for (auto &it : list) {
        if (it < 0 || it > 63) {
            throw sls::RuntimeError(
                "Dbit list value must be between 0 and 63\n");
        }
    }
    std::sort(begin(list), end(list));
    auto last = std::unique(begin(list), end(list));
    list.erase(last, list.end());

    sls::StaticVector<int, MAX_RX_DBIT> arg = list;
    sendToReceiver(F_SET_RECEIVER_DBIT_LIST, arg, nullptr);
}

int Module::getReceiverDbitOffset() const {
    return sendToReceiver<int>(F_GET_RECEIVER_DBIT_OFFSET);
}

void Module::setReceiverDbitOffset(int value) {
    sendToReceiver(F_SET_RECEIVER_DBIT_OFFSET, value, nullptr);
}

void Module::setDigitalIODelay(uint64_t pinMask, int delay) {
    uint64_t args[]{pinMask, static_cast<uint64_t>(delay)};
    sendToDetector(F_DIGITAL_IO_DELAY, args, nullptr);
}

bool Module::getLEDEnable() const {
    return sendToDetector<int>(F_LED, GET_FLAG);
}

void Module::setLEDEnable(bool enable) {
    sendToDetector<int>(F_LED, static_cast<int>(enable));
}

// Pattern

void Module::setPattern(const Pattern &pat) {
    sendToDetector(F_SET_PATTERN, pat.data(), pat.size(), nullptr, 0);
}

Pattern Module::getPattern() {
    Pattern pat;
    sendToDetector(F_GET_PATTERN, nullptr, 0, pat.data(), pat.size());
    return pat;
}

void Module::loadDefaultPattern() { sendToDetector(F_LOAD_DEFAULT_PATTERN); }

uint64_t Module::getPatternIOControl() const {
    return sendToDetector<uint64_t>(F_SET_PATTERN_IO_CONTROL,
                                    int64_t(GET_FLAG));
}

void Module::setPatternIOControl(uint64_t word) {
    sendToDetector<uint64_t>(F_SET_PATTERN_IO_CONTROL, word);
}

uint64_t Module::getPatternWord(int addr) const {
    uint64_t args[]{static_cast<uint64_t>(addr),
                    static_cast<uint64_t>(GET_FLAG)};
    return sendToDetector<uint64_t>(F_SET_PATTERN_WORD, args);
}

void Module::setPatternWord(int addr, uint64_t word) {
    uint64_t args[]{static_cast<uint64_t>(addr), word};
    sendToDetector<uint64_t>(F_SET_PATTERN_WORD, args);
}

std::array<int, 2> Module::getPatternLoopAddresses(int level) const {
    int args[]{level, GET_FLAG, GET_FLAG};
    std::array<int, 2> retvals{};
    sendToDetector(F_SET_PATTERN_LOOP_ADDRESSES, args, retvals);
    return retvals;
}

void Module::setPatternLoopAddresses(int level, int start, int stop) {
    int args[]{level, start, stop};
    std::array<int, 2> retvals{};
    sendToDetector(F_SET_PATTERN_LOOP_ADDRESSES, args, retvals);
}

int Module::getPatternLoopCycles(int level) const {
    int args[]{level, GET_FLAG};
    return sendToDetector<int>(F_SET_PATTERN_LOOP_CYCLES, args);
}

void Module::setPatternLoopCycles(int level, int n) {
    int args[]{level, n};
    sendToDetector<int>(F_SET_PATTERN_LOOP_CYCLES, args);
}

int Module::getPatternWaitAddr(int level) const {
    int args[]{level, GET_FLAG};
    return sendToDetector<int>(F_SET_PATTERN_WAIT_ADDR, args);
}

void Module::setPatternWaitAddr(int level, int addr) {
    int args[]{level, addr};
    sendToDetector<int>(F_SET_PATTERN_WAIT_ADDR, args);
}

uint64_t Module::getPatternWaitTime(int level) const {
    uint64_t args[]{static_cast<uint64_t>(level),
                    static_cast<uint64_t>(GET_FLAG)};
    return sendToDetector<uint64_t>(F_SET_PATTERN_WAIT_TIME, args);
}

void Module::setPatternWaitTime(int level, uint64_t t) {
    uint64_t args[]{static_cast<uint64_t>(level), t};
    sendToDetector<uint64_t>(F_SET_PATTERN_WAIT_TIME, args);
}

uint64_t Module::getPatternMask() const {
    return sendToDetector<uint64_t>(F_GET_PATTERN_MASK);
}

void Module::setPatternMask(uint64_t mask) {
    sendToDetector(F_SET_PATTERN_MASK, mask, nullptr);
}

uint64_t Module::getPatternBitMask() const {
    return sendToDetector<uint64_t>(F_GET_PATTERN_BIT_MASK);
}

void Module::setPatternBitMask(uint64_t mask) {
    sendToDetector(F_SET_PATTERN_BIT_MASK, mask, nullptr);
}

void Module::startPattern() { sendToDetector(F_START_PATTERN); }

// Moench

std::map<std::string, std::string> Module::getAdditionalJsonHeader() const {
    // TODO, refactor this function with a more robust sending.
    // Now assuming whitespace separated key value
    if (!shm()->useReceiverFlag) {
        throw RuntimeError("Set rx_hostname first to use receiver parameters "
                           "(zmq json header)");
    }
    auto client = ReceiverSocket(shm()->rxHostname, shm()->rxTCPPort);
    client.Send(F_GET_ADDITIONAL_JSON_HEADER);
    if (client.Receive<int>() == FAIL) {
        throw RuntimeError("Receiver " + std::to_string(moduleIndex) +
                           " returned error: " + client.readErrorMessage());
    } else {
        auto size = client.Receive<int>();
        std::string buff(size, '\0');
        std::map<std::string, std::string> retval;
        if (size > 0) {
            client.Receive(&buff[0], buff.size());
            std::istringstream iss(buff);
            std::string key, value;
            while (iss >> key) {
                iss >> value;
                retval[key] = value;
            }
        }
        LOG(logDEBUG) << "Getting additional json header " << ToString(retval);
        return retval;
    }
}

void Module::setAdditionalJsonHeader(
    const std::map<std::string, std::string> &jsonHeader) {
    if (!shm()->useReceiverFlag) {
        throw RuntimeError("Set rx_hostname first to use receiver parameters "
                           "(zmq json header)");
    }
    for (auto &it : jsonHeader) {
        if (it.first.empty() || it.first.length() > SHORT_STR_LENGTH ||
            it.second.length() > SHORT_STR_LENGTH) {
            throw RuntimeError(
                it.first + " or " + it.second +
                " pair has invalid size. "
                "Key cannot be empty. Both can have max 20 characters");
        }
    }
    std::ostringstream oss;
    for (auto &it : jsonHeader)
        oss << it.first << ' ' << it.second << ' ';
    auto buff = oss.str();
    const auto size = static_cast<int>(buff.size());
    LOG(logDEBUG) << "Sending to receiver additional json header "
                  << ToString(jsonHeader);
    auto client = ReceiverSocket(shm()->rxHostname, shm()->rxTCPPort);
    client.Send(F_SET_ADDITIONAL_JSON_HEADER);
    client.Send(size);
    if (size > 0)
        client.Send(&buff[0], buff.size());

    if (client.Receive<int>() == FAIL) {
        throw RuntimeError("Receiver " + std::to_string(moduleIndex) +
                           " returned error: " + client.readErrorMessage());
    }
}

std::string Module::getAdditionalJsonParameter(const std::string &key) const {
    char arg[SHORT_STR_LENGTH]{};
    sls::strcpy_safe(arg, key.c_str());
    char retval[SHORT_STR_LENGTH]{};
    sendToReceiver(F_GET_ADDITIONAL_JSON_PARAMETER, arg, retval);
    return retval;
}

void Module::setAdditionalJsonParameter(const std::string &key,
                                        const std::string &value) {
    if (key.empty() || key.length() > SHORT_STR_LENGTH ||
        value.length() > SHORT_STR_LENGTH) {
        throw RuntimeError(
            key + " or " + value +
            " pair has invalid size. "
            "Key cannot be empty. Both can have max 2 characters");
    }
    char args[2][SHORT_STR_LENGTH]{};
    sls::strcpy_safe(args[0], key.c_str());
    sls::strcpy_safe(args[1], value.c_str());
    sendToReceiver(F_SET_ADDITIONAL_JSON_PARAMETER, args, nullptr);
}

// Advanced
void Module::programFPGA(std::vector<char> buffer) {
    switch (shm()->detType) {
    case JUNGFRAU:
    case CHIPTESTBOARD:
    case MOENCH:
        programFPGAviaBlackfin(buffer);
        break;
    case MYTHEN3:
    case GOTTHARD2:
        programFPGAviaNios(buffer);
        break;
    default:
        throw RuntimeError("Program FPGA is not implemented for this detector");
    }
}

void Module::resetFPGA() { sendToDetector(F_RESET_FPGA); }

void Module::copyDetectorServer(const std::string &fname,
                                const std::string &hostname) {
    char args[2][MAX_STR_LENGTH]{};
    sls::strcpy_safe(args[0], fname.c_str());
    sls::strcpy_safe(args[1], hostname.c_str());
    LOG(logINFO) << "Sending detector server " << args[0] << " from host "
                 << args[1];
    sendToDetector(F_COPY_DET_SERVER, args, nullptr);
}

void Module::rebootController() {
    sendToDetector(F_REBOOT_CONTROLLER);
    LOG(logINFO) << "Controller rebooted successfully!";
}

uint32_t Module::readRegister(uint32_t addr) const {
    return sendToDetectorStop<uint32_t>(F_READ_REGISTER, addr);
}

uint32_t Module::writeRegister(uint32_t addr, uint32_t val) {
    uint32_t args[]{addr, val};
    return sendToDetectorStop<uint32_t>(F_WRITE_REGISTER, args);
}

void Module::setBit(uint32_t addr, int n) {
    if (n < 0 || n > 31) {
        throw RuntimeError("Bit number " + std::to_string(n) + " out of Range");
    } else {
        uint32_t val = readRegister(addr);
        writeRegister(addr, val | 1 << n);
    }
}

void Module::clearBit(uint32_t addr, int n) {
    if (n < 0 || n > 31) {
        throw RuntimeError("Bit number " + std::to_string(n) + " out of Range");
    } else {
        uint32_t val = readRegister(addr);
        writeRegister(addr, val & ~(1 << n));
    }
}

int Module::getBit(uint32_t addr, int n) {
    if (n < 0 || n > 31) {
        throw RuntimeError("Bit number " + std::to_string(n) + " out of Range");
    } else {
        return ((readRegister(addr) >> n) & 0x1);
    }
}

void Module::executeFirmwareTest() { sendToDetector(F_SET_FIRMWARE_TEST); }

void Module::executeBusTest() { sendToDetector(F_SET_BUS_TEST); }

void Module::writeAdcRegister(uint32_t addr, uint32_t val) {
    uint32_t args[]{addr, val};
    sendToDetector(F_WRITE_ADC_REG, args, nullptr);
}

uint32_t Module::getADCInvert() const {
    return sendToDetector<uint32_t>(F_GET_ADC_INVERT);
}

void Module::setADCInvert(uint32_t value) {
    sendToDetector(F_SET_ADC_INVERT, value, nullptr);
}

// Insignificant
int Module::getControlPort() const { return shm()->controlPort; }

void Module::setControlPort(int port_number) {
    if (strlen(shm()->hostname) > 0) {
        shm()->controlPort = sendToDetector<int>(F_SET_PORT, port_number);
    } else {
        shm()->controlPort = port_number;
    }
}

int Module::getStopPort() const { return shm()->stopPort; }

void Module::setStopPort(int port_number) {
    if (strlen(shm()->hostname) > 0) {
        shm()->stopPort = sendToDetectorStop<int>(F_SET_PORT, port_number);
    } else {
        shm()->stopPort = port_number;
    }
}

bool Module::getLockDetector() const {
    return sendToDetector<int>(F_LOCK_SERVER, GET_FLAG);
}

void Module::setLockDetector(bool lock) {
    sendToDetector<int>(F_LOCK_SERVER, static_cast<int>(lock));
}

sls::IpAddr Module::getLastClientIP() const {
    return sendToDetector<sls::IpAddr>(F_GET_LAST_CLIENT_IP);
}

std::string Module::execCommand(const std::string &cmd) {
    char arg[MAX_STR_LENGTH]{};
    char retval[MAX_STR_LENGTH]{};
    sls::strcpy_safe(arg, cmd.c_str());
    sendToDetector(F_EXEC_COMMAND, arg, retval);
    return retval;
}

int64_t Module::getNumberOfFramesFromStart() const {
    return sendToDetectorStop<int64_t>(F_GET_FRAMES_FROM_START);
}

int64_t Module::getActualTime() const {
    return sendToDetectorStop<int64_t>(F_GET_ACTUAL_TIME);
}

int64_t Module::getMeasurementTime() const {
    return sendToDetectorStop<int64_t>(F_GET_MEASUREMENT_TIME);
}

uint64_t Module::getReceiverCurrentFrameIndex() const {
    return sendToReceiver<uint64_t>(F_GET_RECEIVER_FRAME_INDEX);
}

// private

void Module::checkArgs(const void *args, size_t args_size, void *retval,
                       size_t retval_size) const {
    if (args == nullptr && args_size != 0)
        throw RuntimeError(
            "Passed nullptr as args to Send function but size is not 0");
    if (args != nullptr && args_size == 0)
        throw RuntimeError(
            "Passed size 0 to Send function but args is not nullptr");
    if (retval == nullptr && retval_size != 0)
        throw RuntimeError(
            "Passed nullptr as retval to Send function but size is not 0");
    if (retval != nullptr && retval_size == 0)
        throw RuntimeError(
            "Passed size 0 to Send function but retval is not nullptr");
}

// Macro to check arguments passed to send to receiver and send to detector
// Should detect and fail on pointer types and on nullptr_t
#define STATIC_ASSERT_ARG(ARG, DST)                                            \
    static_assert(!std::is_pointer<ARG>::value,                                \
                  "Pointer type is incompatible with templated " DST);         \
    static_assert(!std::is_same<ARG, std::nullptr_t>::value,                   \
                  "nullptr_t type is incompatible with templated " DST);

void Module::sendToDetector(int fnum, const void *args, size_t args_size,
                            void *retval, size_t retval_size) const {
    // This is the only function that actually sends data to the detector
    // the other versions use templates to deduce sizes and create
    // the return type
    checkArgs(args, args_size, retval, retval_size);
    auto client = DetectorSocket(shm()->hostname, shm()->controlPort);
    client.sendCommandThenRead(fnum, args, args_size, retval, retval_size);
    client.close();
}

void Module::sendToDetector(int fnum, const void *args, size_t args_size,
                            void *retval, size_t retval_size) {
    static_cast<const Module &>(*this).sendToDetector(fnum, args, args_size,
                                                      retval, retval_size);
}

template <typename Arg, typename Ret>
void Module::sendToDetector(int fnum, const Arg &args, Ret &retval) const {
    LOG(logDEBUG1) << "Sending: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", nullptr, 0, " << typeid(Ret).name() << ", "
                   << sizeof(Ret) << "]";
    STATIC_ASSERT_ARG(Arg, "sendToDetector")
    STATIC_ASSERT_ARG(Ret, "sendToDetector")
    sendToDetector(fnum, &args, sizeof(args), &retval, sizeof(retval));
    LOG(logDEBUG1) << "Got back: " << ToString(retval);
}

template <typename Arg, typename Ret>
void Module::sendToDetector(int fnum, const Arg &args, Ret &retval) {
    static_cast<const Module &>(*this).sendToDetector(fnum, args, retval);
}

template <typename Arg>
void Module::sendToDetector(int fnum, const Arg &args, std::nullptr_t) const {
    LOG(logDEBUG1) << "Sending: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", " << typeid(Arg).name() << ", " << sizeof(Arg)
                   << ", nullptr, 0 ]";
    STATIC_ASSERT_ARG(Arg, "sendToDetector")
    sendToDetector(fnum, &args, sizeof(args), nullptr, 0);
}

template <typename Arg>
void Module::sendToDetector(int fnum, const Arg &args, std::nullptr_t) {
    static_cast<const Module &>(*this).sendToDetector(fnum, args, nullptr);
}

template <typename Ret>
void Module::sendToDetector(int fnum, std::nullptr_t, Ret &retval) const {
    LOG(logDEBUG1) << "Sending: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", nullptr, 0, " << typeid(Ret).name() << ", "
                   << sizeof(Ret) << "]";
    STATIC_ASSERT_ARG(Ret, "sendToDetector")
    sendToDetector(fnum, nullptr, 0, &retval, sizeof(retval));
    LOG(logDEBUG1) << "Got back: " << ToString(retval);
}

template <typename Ret>
void Module::sendToDetector(int fnum, std::nullptr_t, Ret &retval) {
    static_cast<const Module &>(*this).sendToDetector(fnum, nullptr, retval);
}

void Module::sendToDetector(int fnum) const {
    LOG(logDEBUG1) << "Sending: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << "]";
    sendToDetector(fnum, nullptr, 0, nullptr, 0);
}

void Module::sendToDetector(int fnum) {
    static_cast<const Module &>(*this).sendToDetector(fnum);
}

template <typename Ret> Ret Module::sendToDetector(int fnum) const {
    LOG(logDEBUG1) << "Sending: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", nullptr, 0, " << typeid(Ret).name() << ", "
                   << sizeof(Ret) << "]";
    STATIC_ASSERT_ARG(Ret, "sendToDetector")
    Ret retval{};
    sendToDetector(fnum, nullptr, 0, &retval, sizeof(retval));
    LOG(logDEBUG1) << "Got back: " << ToString(retval);
    return retval;
}

template <typename Ret> Ret Module::sendToDetector(int fnum) {
    return static_cast<const Module &>(*this).sendToDetector<Ret>(fnum);
}

template <typename Ret, typename Arg>
Ret Module::sendToDetector(int fnum, const Arg &args) const {
    LOG(logDEBUG1) << "Sending: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", " << args << ", " << sizeof(args) << ", "
                   << typeid(Ret).name() << ", " << sizeof(Ret) << "]";
    STATIC_ASSERT_ARG(Arg, "sendToDetector")
    STATIC_ASSERT_ARG(Ret, "sendToDetector")
    Ret retval{};
    sendToDetector(fnum, &args, sizeof(args), &retval, sizeof(retval));
    LOG(logDEBUG1) << "Got back: " << ToString(retval);
    return retval;
}

template <typename Ret, typename Arg>
Ret Module::sendToDetector(int fnum, const Arg &args) {
    return static_cast<const Module &>(*this).sendToDetector<Ret>(fnum, args);
}

//---------------------------------------------------------- sendToDetectorStop

void Module::sendToDetectorStop(int fnum, const void *args, size_t args_size,
                                void *retval, size_t retval_size) const {
    // This is the only function that actually sends data to the detector stop
    // the other versions use templates to deduce sizes and create
    // the return type
    checkArgs(args, args_size, retval, retval_size);
    auto stop = DetectorSocket(shm()->hostname, shm()->stopPort);
    stop.sendCommandThenRead(fnum, args, args_size, retval, retval_size);
    stop.close();
}

void Module::sendToDetectorStop(int fnum, const void *args, size_t args_size,
                                void *retval, size_t retval_size) {
    static_cast<const Module &>(*this).sendToDetectorStop(fnum, args, args_size,
                                                          retval, retval_size);
}

template <typename Arg, typename Ret>
void Module::sendToDetectorStop(int fnum, const Arg &args, Ret &retval) const {
    LOG(logDEBUG1) << "Sending to Stop: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", " << args << ", " << sizeof(args) << ", "
                   << typeid(Ret).name() << ", " << sizeof(Ret) << "]";
    STATIC_ASSERT_ARG(Arg, "sendToDetectorStop")
    STATIC_ASSERT_ARG(Ret, "sendToDetectorStop")
    sendToDetectorStop(fnum, &args, sizeof(args), &retval, sizeof(retval));
    LOG(logDEBUG1) << "Got back: " << ToString(retval);
}

template <typename Arg, typename Ret>
void Module::sendToDetectorStop(int fnum, const Arg &args, Ret &retval) {
    static_cast<const Module &>(*this).sendToDetectorStop(fnum, args, retval);
}

template <typename Arg>
void Module::sendToDetectorStop(int fnum, const Arg &args,
                                std::nullptr_t) const {
    LOG(logDEBUG1) << "Sending to Stop: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", " << typeid(Arg).name() << ", " << sizeof(Arg)
                   << ", nullptr, 0 ]";
    STATIC_ASSERT_ARG(Arg, "sendToDetectorStop")
    sendToDetectorStop(fnum, &args, sizeof(args), nullptr, 0);
}

template <typename Arg>
void Module::sendToDetectorStop(int fnum, const Arg &args, std::nullptr_t) {
    static_cast<const Module &>(*this).sendToDetectorStop(fnum, args, nullptr);
}

template <typename Ret>
void Module::sendToDetectorStop(int fnum, std::nullptr_t, Ret &retval) const {
    LOG(logDEBUG1) << "Sending to Stop: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", nullptr, 0, " << typeid(Ret).name() << ", "
                   << sizeof(Ret) << "]";
    STATIC_ASSERT_ARG(Ret, "sendToDetectorStop")
    sendToDetectorStop(fnum, nullptr, 0, &retval, sizeof(retval));
    LOG(logDEBUG1) << "Got back: " << retval;
}

template <typename Ret>
void Module::sendToDetectorStop(int fnum, std::nullptr_t, Ret &retval) {
    static_cast<const Module &>(*this).sendToDetectorStop(fnum, nullptr,
                                                          retval);
}

void Module::sendToDetectorStop(int fnum) const {
    LOG(logDEBUG1) << "Sending to Stop: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", nullptr, 0, nullptr, 0]";
    sendToDetectorStop(fnum, nullptr, 0, nullptr, 0);
}

void Module::sendToDetectorStop(int fnum) {
    static_cast<const Module &>(*this).sendToDetectorStop(fnum);
}

template <typename Ret> Ret Module::sendToDetectorStop(int fnum) const {
    LOG(logDEBUG1) << "Sending to Stop: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", nullptr, 0, " << typeid(Ret).name() << ", "
                   << sizeof(Ret) << "]";
    STATIC_ASSERT_ARG(Ret, "sendToDetectorStop")
    Ret retval{};
    sendToDetectorStop(fnum, nullptr, 0, &retval, sizeof(retval));
    LOG(logDEBUG1) << "Got back: " << retval;
    return retval;
}

template <typename Ret> Ret Module::sendToDetectorStop(int fnum) {
    return static_cast<const Module &>(*this).sendToDetectorStop<Ret>(fnum);
}

template <typename Ret, typename Arg>
Ret Module::sendToDetectorStop(int fnum, const Arg &args) const {
    LOG(logDEBUG1) << "Sending to Stop: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", " << args << ", " << sizeof(args) << ", "
                   << typeid(Ret).name() << ", " << sizeof(Ret) << "]";
    STATIC_ASSERT_ARG(Arg, "sendToDetectorStop")
    STATIC_ASSERT_ARG(Ret, "sendToDetectorStop")
    Ret retval{};
    sendToDetectorStop(fnum, &args, sizeof(args), &retval, sizeof(retval));
    LOG(logDEBUG1) << "Got back: " << ToString(retval);
    return retval;
}

template <typename Ret, typename Arg>
Ret Module::sendToDetectorStop(int fnum, const Arg &args) {
    return static_cast<const Module &>(*this).sendToDetectorStop<Ret>(fnum,
                                                                      args);
}

//-------------------------------------------------------------- sendToReceiver

void Module::sendToReceiver(int fnum, const void *args, size_t args_size,
                            void *retval, size_t retval_size) const {
    // This is the only function that actually sends data to the receiver
    // the other versions use templates to deduce sizes and create
    // the return type
    if (!shm()->useReceiverFlag) {
        std::ostringstream oss;
        oss << "Set rx_hostname first to use receiver parameters, ";
        oss << getFunctionNameFromEnum(static_cast<detFuncs>(fnum));
        throw RuntimeError(oss.str());
    }
    checkArgs(args, args_size, retval, retval_size);
    auto receiver = ReceiverSocket(shm()->rxHostname, shm()->rxTCPPort);
    receiver.sendCommandThenRead(fnum, args, args_size, retval, retval_size);
    receiver.close();
}

void Module::sendToReceiver(int fnum, const void *args, size_t args_size,
                            void *retval, size_t retval_size) {
    static_cast<const Module &>(*this).sendToReceiver(fnum, args, args_size,
                                                      retval, retval_size);
}

template <typename Arg, typename Ret>
void Module::sendToReceiver(int fnum, const Arg &args, Ret &retval) const {
    LOG(logDEBUG1) << "Sending to Receiver: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", " << args << ", " << sizeof(args) << ", "
                   << typeid(Ret).name() << ", " << sizeof(Ret) << "]";
    STATIC_ASSERT_ARG(Arg, "sendToReceiver")
    STATIC_ASSERT_ARG(Ret, "sendToReceiver")
    sendToReceiver(fnum, &args, sizeof(args), &retval, sizeof(retval));
    LOG(logDEBUG1) << "Got back: " << retval;
}

template <typename Arg, typename Ret>
void Module::sendToReceiver(int fnum, const Arg &args, Ret &retval) {
    static_cast<const Module &>(*this).sendToReceiver(fnum, args, retval);
}

template <typename Arg>
void Module::sendToReceiver(int fnum, const Arg &args, std::nullptr_t) const {
    LOG(logDEBUG1) << "Sending to Receiver: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", " << typeid(Arg).name() << ", " << sizeof(Arg)
                   << ", nullptr, 0 ]";
    STATIC_ASSERT_ARG(Arg, "sendToReceiver")
    sendToReceiver(fnum, &args, sizeof(args), nullptr, 0);
}

template <typename Arg>
void Module::sendToReceiver(int fnum, const Arg &args, std::nullptr_t) {
    static_cast<const Module &>(*this).sendToReceiver(fnum, args, nullptr);
}

template <typename Ret>
void Module::sendToReceiver(int fnum, std::nullptr_t, Ret &retval) const {
    LOG(logDEBUG1) << "Sending to Receiver: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", nullptr, 0, " << typeid(Ret).name() << ", "
                   << sizeof(Ret) << "]";
    STATIC_ASSERT_ARG(Ret, "sendToReceiver")
    sendToReceiver(fnum, nullptr, 0, &retval, sizeof(retval));
    LOG(logDEBUG1) << "Got back: " << ToString(retval);
}

template <typename Ret>
void Module::sendToReceiver(int fnum, std::nullptr_t, Ret &retval) {
    static_cast<const Module &>(*this).sendToReceiver(fnum, nullptr, retval);
}

template <typename Ret> Ret Module::sendToReceiver(int fnum) const {
    LOG(logDEBUG1) << "Sending to Receiver: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", nullptr, 0, " << typeid(Ret).name() << ", "
                   << sizeof(Ret) << "]";
    STATIC_ASSERT_ARG(Ret, "sendToReceiver")
    Ret retval{};
    sendToReceiver(fnum, nullptr, 0, &retval, sizeof(retval));
    LOG(logDEBUG1) << "Got back: " << ToString(retval);
    return retval;
}

template <typename Ret> Ret Module::sendToReceiver(int fnum) {
    return static_cast<const Module &>(*this).sendToReceiver<Ret>(fnum);
}

void Module::sendToReceiver(int fnum) const {
    LOG(logDEBUG1) << "Sending to Receiver: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", nullptr, 0, nullptr, 0]";
    sendToReceiver(fnum, nullptr, 0, nullptr, 0);
}

void Module::sendToReceiver(int fnum) {
    static_cast<const Module &>(*this).sendToReceiver(fnum);
}

template <typename Ret, typename Arg>
Ret Module::sendToReceiver(int fnum, const Arg &args) const {
    LOG(logDEBUG1) << "Sending to Receiver: ["
                   << getFunctionNameFromEnum(static_cast<detFuncs>(fnum))
                   << ", " << args << ", " << sizeof(args) << ", "
                   << typeid(Ret).name() << ", " << sizeof(Ret) << "]";
    STATIC_ASSERT_ARG(Arg, "sendToReceiver")
    STATIC_ASSERT_ARG(Ret, "sendToReceiver")
    Ret retval{};
    sendToReceiver(fnum, &args, sizeof(args), &retval, sizeof(retval));
    LOG(logDEBUG1) << "Got back: " << retval;
    return retval;
}

template <typename Ret, typename Arg>
Ret Module::sendToReceiver(int fnum, const Arg &args) {
    return static_cast<const Module &>(*this).sendToReceiver<Ret>(fnum, args);
}

slsDetectorDefs::detectorType Module::getDetectorTypeFromShm(int det_id,
                                                             bool verify) {
    if (!shm.IsExisting()) {
        throw SharedMemoryError("Shared memory " + shm.GetName() +
                                "does not exist.\n Corrupted Multi Shared "
                                "memory. Please free shared memory.");
    }

    shm.OpenSharedMemory();
    if (verify && shm()->shmversion != MODULE_SHMVERSION) {
        std::ostringstream ss;
        ss << "Single shared memory (" << det_id << "-" << moduleIndex
           << ":)version mismatch (expected 0x" << std::hex << MODULE_SHMVERSION
           << " but got 0x" << shm()->shmversion << ")" << std::dec
           << ". Clear Shared memory to continue.";
        shm.UnmapSharedMemory();
        throw SharedMemoryError(ss.str());
    }
    return shm()->detType;
}

void Module::initSharedMemory(detectorType type, int det_id, bool verify) {
    shm = SharedMemory<sharedModule>(det_id, moduleIndex);
    if (!shm.IsExisting()) {
        shm.CreateSharedMemory();
        initializeModuleStructure(type);
    } else {
        shm.OpenSharedMemory();
        if (verify && shm()->shmversion != MODULE_SHMVERSION) {
            std::ostringstream ss;
            ss << "Single shared memory (" << det_id << "-" << moduleIndex
               << ":) version mismatch (expected 0x" << std::hex
               << MODULE_SHMVERSION << " but got 0x" << shm()->shmversion << ")"
               << std::dec << ". Clear Shared memory to continue.";
            throw SharedMemoryError(ss.str());
        }
    }
}

void Module::initializeModuleStructure(detectorType type) {
    shm()->shmversion = MODULE_SHMVERSION;
    memset(shm()->hostname, 0, MAX_STR_LENGTH);
    shm()->detType = type;
    shm()->numberOfModule.x = 0;
    shm()->numberOfModule.y = 0;
    shm()->controlPort = DEFAULT_PORTNO;
    shm()->stopPort = DEFAULT_PORTNO + 1;
    sls::strcpy_safe(shm()->settingsDir, getenv("HOME"));
    sls::strcpy_safe(shm()->rxHostname, "none");
    shm()->rxTCPPort = DEFAULT_PORTNO + 2;
    shm()->useReceiverFlag = false;
    shm()->zmqport = DEFAULT_ZMQ_CL_PORTNO +
                     (moduleIndex * ((shm()->detType == EIGER) ? 2 : 1));
    shm()->zmqip = IpAddr{};
    shm()->numUDPInterfaces = 1;
    shm()->stoppedFlag = false;

    // get the Module parameters based on type
    detParameters parameters{type};
    shm()->nChan.x = parameters.nChanX;
    shm()->nChan.y = parameters.nChanY;
    shm()->nChip.x = parameters.nChipX;
    shm()->nChip.y = parameters.nChipY;
    shm()->nDacs = parameters.nDacs;
}

void Module::checkDetectorVersionCompatibility() {
    int64_t arg = 0;
    switch (shm()->detType) {
    case EIGER:
        arg = APIEIGER;
        break;
    case JUNGFRAU:
        arg = APIJUNGFRAU;
        break;
    case GOTTHARD:
        arg = APIGOTTHARD;
        break;
    case CHIPTESTBOARD:
        arg = APICTB;
        break;
    case MOENCH:
        arg = APIMOENCH;
        break;
    case MYTHEN3:
        arg = APIMYTHEN3;
        break;
    case GOTTHARD2:
        arg = APIGOTTHARD2;
        break;
    default:
        throw NotImplementedError(
            "Check version compatibility is not implemented for this detector");
    }
    sendToDetector(F_CHECK_VERSION, arg, nullptr);
    sendToDetectorStop(F_CHECK_VERSION, arg, nullptr);
}

void Module::checkReceiverVersionCompatibility() {
    // TODO! Verify that this works as intended when version don't match
    sendToReceiver(F_RECEIVER_CHECK_VERSION, int64_t(APIRECEIVER), nullptr);
}

int Module::sendModule(sls_detector_module *myMod, sls::ClientSocket &client) {
    constexpr TLogLevel level = logDEBUG1;
    LOG(level) << "Sending Module";
    int ts = 0;
    int n = 0;
    n = client.Send(&(myMod->serialnumber), sizeof(myMod->serialnumber));
    ts += n;
    LOG(level) << "Serial number sent. " << n
               << " bytes. serialno: " << myMod->serialnumber;

    n = client.Send(&(myMod->nchan), sizeof(myMod->nchan));
    ts += n;
    LOG(level) << "nchan sent. " << n << " bytes. nchan: " << myMod->nchan;

    n = client.Send(&(myMod->nchip), sizeof(myMod->nchip));
    ts += n;
    LOG(level) << "nchip sent. " << n << " bytes. nchip: " << myMod->nchip;

    n = client.Send(&(myMod->ndac), sizeof(myMod->ndac));
    ts += n;
    LOG(level) << "ndac sent. " << n << " bytes. ndac: " << myMod->ndac;

    n = client.Send(&(myMod->reg), sizeof(myMod->reg));
    ts += n;
    LOG(level) << "reg sent. " << n << " bytes. reg: " << myMod->reg;

    n = client.Send(&(myMod->iodelay), sizeof(myMod->iodelay));
    ts += n;
    LOG(level) << "iodelay sent. " << n
               << " bytes. iodelay: " << myMod->iodelay;

    n = client.Send(&(myMod->tau), sizeof(myMod->tau));
    ts += n;
    LOG(level) << "tau sent. " << n << " bytes. tau: " << myMod->tau;

    n = client.Send(myMod->eV, sizeof(myMod->eV));
    ts += n;
    LOG(level) << "ev sent. " << n << " bytes. ev: " << ToString(myMod->eV);

    n = client.Send(myMod->dacs, sizeof(int) * (myMod->ndac));
    ts += n;
    LOG(level) << "dacs sent. " << n << " bytes";

    if (shm()->detType == EIGER || shm()->detType == MYTHEN3) {
        n = client.Send(myMod->chanregs, sizeof(int) * (myMod->nchan));
        ts += n;
        LOG(level) << "channels sent. " << n << " bytes";
    }
    return ts;
}

void Module::setModule(sls_detector_module &module, bool trimbits) {
    LOG(logDEBUG1) << "Setting module with trimbits:" << trimbits;
    // to exclude trimbits
    if (!trimbits) {
        module.nchan = 0;
        module.nchip = 0;
    }
    auto client = DetectorSocket(shm()->hostname, shm()->controlPort);
    client.Send(F_SET_MODULE);
    sendModule(&module, client);
    if (client.Receive<int>() == FAIL) {
        throw RuntimeError("Detector " + std::to_string(moduleIndex) +
                           " returned error: " + client.readErrorMessage());
    }
}

void Module::updateReceiverStreamingIP() {
    auto ip = getReceiverStreamingIP();
    if (ip == 0) {
        // Hostname could be ip try to decode otherwise look up the hostname
        ip = sls::IpAddr{shm()->rxHostname};
        if (ip == 0) {
            ip = HostnameToIp(shm()->rxHostname);
        }
        LOG(logINFO) << "Setting default receiver " << moduleIndex
                     << " streaming zmq ip to " << ip;
    }
    setReceiverStreamingIP(ip);
}

void Module::updateRateCorrection() {
    sendToDetector(F_UPDATE_RATE_CORRECTION);
}

sls_detector_module Module::interpolateTrim(sls_detector_module *a,
                                            sls_detector_module *b,
                                            const int energy, const int e1,
                                            const int e2, bool trimbits) {
    // dacs specified only for eiger and mythen3
    if (shm()->detType != EIGER && shm()->detType != MYTHEN3) {
        throw NotImplementedError(
            "Interpolation of Trim values not implemented for this detector!");
    }

    sls_detector_module myMod{shm()->detType};
    enum eiger_DacIndex {
        E_SVP,
        E_VTR,
        E_VRF,
        E_VRS,
        E_SVN,
        E_VTGSTV,
        E_VCMP_LL,
        E_VCMP_LR,
        E_CAL,
        E_VCMP_RL,
        E_RXB_RB,
        E_RXB_LB,
        E_VCMP_RR,
        E_VCP,
        E_VCN,
        E_VIS
    };
    enum mythen3_DacIndex {
        M_VCASSH,
        M_VTH2,
        M_VRSHAPER,
        M_VRSHAPER_N,
        M_VIPRE_OUT,
        M_VTH3,
        M_VTH1,
        M_VICIN,
        M_VCAS,
        M_VRPREAMP,
        M_VCAL_N,
        M_VIPRE,
        M_VISHAPER,
        M_VCAL_P,
        M_VTRIM,
        M_VDCSH
    };

    // create copy and interpolate dac lists
    std::vector<int> dacs_to_copy, dacs_to_interpolate;
    if (shm()->detType == EIGER) {
        dacs_to_copy.insert(
            dacs_to_copy.end(),
            {E_SVP, E_VTR, E_SVN, E_VTGSTV, E_RXB_RB, E_RXB_LB, E_VCN, E_VIS});
        // interpolate vrf, vcmp, vcp
        dacs_to_interpolate.insert(
            dacs_to_interpolate.end(),
            {E_VRF, E_VCMP_LL, E_VCMP_LR, E_VCMP_RL, E_VCMP_RR, E_VCP, E_VRS});
    } else {
        dacs_to_copy.insert(dacs_to_copy.end(),
                            {M_VCASSH, M_VRSHAPER, M_VRSHAPER_N, M_VIPRE_OUT,
                             M_VICIN, M_VCAS, M_VRPREAMP, M_VCAL_N, M_VIPRE,
                             M_VISHAPER, M_VCAL_P, M_VDCSH});
        // interpolate vtrim, vth1, vth2, vth3
        dacs_to_interpolate.insert(dacs_to_interpolate.end(),
                                   {M_VTH1, M_VTH2, M_VTH3, M_VTRIM});
    }

    // Copy Dacs
    for (size_t i = 0; i < dacs_to_copy.size(); ++i) {
        if (a->dacs[dacs_to_copy[i]] != b->dacs[dacs_to_copy[i]]) {
            throw RuntimeError("Interpolate module: dacs " + std::to_string(i) +
                               " different");
        }
        myMod.dacs[dacs_to_copy[i]] = a->dacs[dacs_to_copy[i]];
    }

    // Interpolate Dacs
    for (size_t i = 0; i < dacs_to_interpolate.size(); ++i) {
        myMod.dacs[dacs_to_interpolate[i]] =
            linearInterpolation(energy, e1, e2, a->dacs[dacs_to_interpolate[i]],
                                b->dacs[dacs_to_interpolate[i]]);
    }

    // Copy irrelevant dacs (without failing)
    if (shm()->detType == EIGER) {
        // CAL
        if (a->dacs[E_CAL] != b->dacs[E_CAL]) {
            LOG(logWARNING)
                << "DAC CAL differs in both energies (" << a->dacs[E_CAL] << ","
                << b->dacs[E_CAL] << ")!\nTaking first: " << a->dacs[E_CAL];
        }
        myMod.dacs[E_CAL] = a->dacs[E_CAL];
    }

    // Interpolate all trimbits
    if (trimbits) {
        for (int i = 0; i < myMod.nchan; ++i) {
            myMod.chanregs[i] = linearInterpolation(
                energy, e1, e2, a->chanregs[i], b->chanregs[i]);
        }
    }
    return myMod;
}

std::string Module::getTrimbitFilename(detectorSettings s, int e_eV) {
    std::string ssettings;
    switch (s) {
    case STANDARD:
        ssettings = "/standard";
        break;
    case FAST:
        ssettings = "/fast";
        break;
    case HIGHGAIN:
        ssettings = "/highgain";
        break;
    case LOWGAIN:
        ssettings = "/lowgain";
        break;
    case VERYHIGHGAIN:
        ssettings = "/veryhighgain";
        break;
    case VERYLOWGAIN:
        ssettings = "/verylowgain";
        break;
    default:
        std::ostringstream ss;
        ss << "Unknown settings " << ToString(s) << " for this detector!";
        throw RuntimeError(ss.str());
    }
    std::ostringstream ostfn;
    ostfn << shm()->settingsDir << ssettings << "/" << e_eV << "eV";
    if (shm()->detType == EIGER) {
        ostfn << "/noise.sn";
    } else if (shm()->detType == MYTHEN3) {
        ostfn << "/trim.sn";
    } else {
        throw RuntimeError(
            "Settings or trimbit files not defined for this detector.");
    }
    int moduleIdWidth = 3;
    if (shm()->detType == MYTHEN3) {
        moduleIdWidth = 4;
    }
    ostfn << std::setfill('0') << std::setw(moduleIdWidth) << std::dec
          << getModuleId() << std::setbase(10);
    return ostfn.str();
}

sls_detector_module Module::readSettingsFile(const std::string &fname,
                                             bool trimbits) {
    LOG(logDEBUG1) << "Read settings file " << fname;
    sls_detector_module myMod(shm()->detType);
    // open file
    std::ifstream infile;
    if (shm()->detType == EIGER || shm()->detType == MYTHEN3) {
        infile.open(fname.c_str(), std::ifstream::binary);
    } else {
        infile.open(fname.c_str(), std::ios_base::in);
    }
    if (!infile) {
        throw RuntimeError("Could not open settings file: " + fname);
    }

    auto file_size = getFileSize(infile);

    // eiger
    if (shm()->detType == EIGER) {
        infile.read(reinterpret_cast<char *>(myMod.dacs),
                    sizeof(int) * (myMod.ndac));
        infile.read(reinterpret_cast<char *>(&myMod.iodelay),
                    sizeof(myMod.iodelay));
        infile.read(reinterpret_cast<char *>(&myMod.tau), sizeof(myMod.tau));
        for (int i = 0; i < myMod.ndac; ++i) {
            LOG(logDEBUG1) << "dac " << i << ":" << myMod.dacs[i];
        }
        LOG(logDEBUG1) << "iodelay:" << myMod.iodelay;
        LOG(logDEBUG1) << "tau:" << myMod.tau;
        if (trimbits) {
            infile.read(reinterpret_cast<char *>(myMod.chanregs),
                        sizeof(int) * (myMod.nchan));
        }
        if (!infile) {
            throw RuntimeError("readSettingsFile: Could not load all values "
                               "for settings for " +
                               fname);
        }
    }

    // mythen3 (dacs, trimbits)
    else if (shm()->detType == MYTHEN3) {
        int expected_size = sizeof(int) * myMod.ndac +
                            sizeof(int) * myMod.nchan + sizeof(myMod.reg);
        if (file_size != expected_size) {
            throw RuntimeError("The size of the settings file: " + fname +
                               " differs from the expected size, " +
                               std::to_string(file_size) + " instead of " +
                               std::to_string(expected_size) + " bytes");
        }
        infile.read(reinterpret_cast<char *>(&myMod.reg), sizeof(myMod.reg));
        infile.read(reinterpret_cast<char *>(myMod.dacs),
                    sizeof(int) * (myMod.ndac));
        for (int i = 0; i < myMod.ndac; ++i) {
            LOG(logDEBUG) << "dac " << i << ":" << myMod.dacs[i];
        }
        if (trimbits) {
            infile.read(reinterpret_cast<char *>(myMod.chanregs),
                        sizeof(int) * (myMod.nchan));
        }
        if (!infile) {
            throw RuntimeError("readSettingsFile: Could not load all values "
                               "for settings for " +
                               fname);
        }
    }

    else {
        throw RuntimeError("Not implemented for this detector");
    }
    LOG(logINFO) << "Settings file loaded: " << fname.c_str();
    return myMod;
}

void Module::programFPGAviaBlackfin(std::vector<char> buffer) {
    // send program from memory to detector
    LOG(logINFO) << "Sending programming binary (from pof) to module "
                 << moduleIndex << " (" << shm()->hostname << ")";
    auto client = DetectorSocket(shm()->hostname, shm()->controlPort);
    client.Send(F_PROGRAM_FPGA);
    uint64_t filesize = buffer.size();
    client.Send(filesize);

    // checksum
    std::string checksum = sls::md5_calculate_checksum(buffer.data(), filesize);
    LOG(logDEBUG1) << "Checksum:" << checksum;
    char cChecksum[MAX_STR_LENGTH];
    memset(cChecksum, 0, MAX_STR_LENGTH);
    strcpy(cChecksum, checksum.c_str());
    client.Send(cChecksum);

    //  opening file fail
    if (client.Receive<int>() == FAIL) {
        std::cout << '\n';
        std::ostringstream os;
        os << "Module " << moduleIndex << " (" << shm()->hostname << ")"
           << " returned error: " << client.readErrorMessage();
        throw RuntimeError(os.str());
    }

    // sending program in parts of 2mb each
    uint64_t unitprogramsize = 0;
    int currentPointer = 0;
    while (filesize > 0) {
        unitprogramsize = MAX_FPGAPROGRAMSIZE; // 2mb
        if (unitprogramsize > filesize) {      // less than 2mb
            unitprogramsize = filesize;
        }
        LOG(logDEBUG) << "unitprogramsize:" << unitprogramsize
                      << "\t filesize:" << filesize;

        client.Send(&buffer[currentPointer], unitprogramsize);
        if (client.Receive<int>() == FAIL) {
            std::cout << '\n';
            std::ostringstream os;
            os << "Module " << moduleIndex << " (" << shm()->hostname << ")"
               << " returned error: " << client.readErrorMessage();
            throw RuntimeError(os.str());
        }
        filesize -= unitprogramsize;
        currentPointer += unitprogramsize;
    }

    // checksum 
    if (client.Receive<int>() == FAIL) {
        std::ostringstream os;
        os << "Module " << moduleIndex << " (" << shm()->hostname << ")"
           << " returned error: " << client.readErrorMessage();
        throw RuntimeError(os.str());
    }
    LOG(logINFO) << "Checksum verified for module " << moduleIndex << " ("
                 << shm()->hostname << ")";

    // simulating erasing flash
    {
        LOG(logINFO) << "(Simulating) Erasing Flash for module " << moduleIndex << " ("
                    << shm()->hostname << ")";
        printf("%d%%\r", 0);
        std::cout << std::flush;
        // erasing takes 65 seconds, printing here (otherwise need threads
        // in server-unnecessary)
        const int ERASE_TIME = 65;
        int count = ERASE_TIME + 1;
        while (count > 0) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            --count;
            printf(
                "%d%%\r",
                static_cast<int>(
                    (static_cast<double>(ERASE_TIME - count) / ERASE_TIME) * 100));
            std::cout << std::flush;
        }
        printf("\n");
    }

    // simulating writing to flash
    {
        LOG(logINFO) << "(Simulating) Writing to Flash for module " << moduleIndex << " (" << shm()->hostname << ")";
        printf("%d%%\r", 0);
        std::cout << std::flush;
        // writing takes 30 seconds, printing here (otherwise need threads
        // in server-unnecessary)
        const int ERASE_TIME = 30;
        int count = ERASE_TIME + 1;
        while (count > 0) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            --count;
            printf(
                "%d%%\r",
                static_cast<int>(
                    (static_cast<double>(ERASE_TIME - count) / ERASE_TIME) * 100));
            std::cout << std::flush;
        }
        printf("\n");
    }

    if (client.Receive<int>() == FAIL) {
        std::ostringstream os;
        os << "Module " << moduleIndex << " (" << shm()->hostname << ")"
           << " returned error: " << client.readErrorMessage();
        throw RuntimeError(os.str());
    }
    if (moduleIndex == 0) {
        LOG(logINFO) << "Copied to flash and checksum verified";
    }

    LOG(logINFO) << "FPGA programmed successfully";
    rebootController();
}

void Module::programFPGAviaNios(std::vector<char> buffer) {
    LOG(logINFO) << "Sending programming binary (from rbf) to Module "
                 << moduleIndex << " (" << shm()->hostname << ")";

    auto client = DetectorSocket(shm()->hostname, shm()->controlPort);
    client.Send(F_PROGRAM_FPGA);
    uint64_t filesize = buffer.size();
    client.Send(filesize);

    // checksum
    std::string checksum = sls::md5_calculate_checksum(buffer.data(), filesize);
    LOG(logDEBUG1) << "Checksum:" << checksum;
    char cChecksum[MAX_STR_LENGTH];
    memset(cChecksum, 0, MAX_STR_LENGTH);
    strcpy(cChecksum, checksum.c_str());
    client.Send(cChecksum);

    // validate file size before sending program
    if (client.Receive<int>() == FAIL) {
        std::ostringstream os;
        os << "Module " << moduleIndex << " (" << shm()->hostname << ")"
           << " returned error: " << client.readErrorMessage();
        throw RuntimeError(os.str());
    }
    client.Send(buffer);

    // simulating erasing flash
    {
        LOG(logINFO) << "(Simulating) Erasing Flash for module " << moduleIndex << " ("
                    << shm()->hostname << ")";
        printf("%d%%\r", 0);
        std::cout << std::flush;
        // erasing takes 10 seconds, printing here (otherwise need threads
        // in server-unnecessary)
        const int ERASE_TIME = 10;
        int count = ERASE_TIME + 1;
        while (count > 0) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            --count;
            printf(
                "%d%%\r",
                static_cast<int>(
                    (static_cast<double>(ERASE_TIME - count) / ERASE_TIME) * 100));
            std::cout << std::flush;
        }
        printf("\n");
    }

    // simulating writing to flash
    {
        LOG(logINFO) << "(Simulating) Writing to Flash for module " << moduleIndex << " (" << shm()->hostname << ")";
        printf("%d%%\r", 0);
        std::cout << std::flush;
        // writing takes 45 seconds, printing here (otherwise need threads
        // in server-unnecessary)
        const int ERASE_TIME = 45;
        int count = ERASE_TIME + 1;
        while (count > 0) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            --count;
            printf(
                "%d%%\r",
                static_cast<int>(
                    (static_cast<double>(ERASE_TIME - count) / ERASE_TIME) * 100));
            std::cout << std::flush;
        }
        printf("\n");
    }
    if (client.Receive<int>() == FAIL) {
        std::ostringstream os;
        os << "Module " << moduleIndex << " (" << shm()->hostname << ")"
           << " returned error: " << client.readErrorMessage();
        throw RuntimeError(os.str());
    }
    LOG(logINFO) << "FPGA programmed successfully";
    rebootController();
}
} // namespace sls
