Continue work on remote control server
This commit is contained in:
parent
ea5d3c3a1d
commit
60f7df6fd6
|
@ -9,6 +9,7 @@
|
||||||
#ifndef PUZZLEFW_INTERRUPT_MANAGER_H_
|
#ifndef PUZZLEFW_INTERRUPT_MANAGER_H_
|
||||||
#define PUZZLEFW_INTERRUPT_MANAGER_H_
|
#define PUZZLEFW_INTERRUPT_MANAGER_H_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
|
@ -21,19 +21,6 @@
|
||||||
#include "data_server.hpp"
|
#include "data_server.hpp"
|
||||||
|
|
||||||
|
|
||||||
/** Convert TriggerMode to string description. */
|
|
||||||
std::string trigger_mode_to_string(puzzlefw::TriggerMode mode)
|
|
||||||
{
|
|
||||||
using puzzlefw::TriggerMode;
|
|
||||||
switch (mode) {
|
|
||||||
case TriggerMode::TRIG_AUTO: return "auto";
|
|
||||||
case TriggerMode::TRIG_EXTERNAL: return "external";
|
|
||||||
case TriggerMode::TRIG_EXTERNAL_ONCE: return "external-once";
|
|
||||||
default: return "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Show firmware status. */
|
/** Show firmware status. */
|
||||||
void show_status(puzzlefw::PuzzleFwDevice& device)
|
void show_status(puzzlefw::PuzzleFwDevice& device)
|
||||||
{
|
{
|
||||||
|
|
|
@ -95,6 +95,18 @@ enum TriggerMode {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** Convert TriggerMode to string description. */
|
||||||
|
inline std::string trigger_mode_to_string(TriggerMode mode)
|
||||||
|
{
|
||||||
|
switch (mode) {
|
||||||
|
case TriggerMode::TRIG_AUTO: return "AUTO";
|
||||||
|
case TriggerMode::TRIG_EXTERNAL: return "EXTERNAL";
|
||||||
|
case TriggerMode::TRIG_EXTERNAL_ONCE: return "EXTERNAL_ONCE";
|
||||||
|
default: return "NONE";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Firmware version information. */
|
/** Firmware version information. */
|
||||||
struct VersionInfo {
|
struct VersionInfo {
|
||||||
uint8_t api_version;
|
uint8_t api_version;
|
||||||
|
|
|
@ -9,9 +9,12 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
#include <istream>
|
#include <istream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -50,6 +53,55 @@ std::string str_to_lower(const std::string& value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** String formatting. */
|
||||||
|
std::string str_format(const char *format, ...)
|
||||||
|
__attribute__ ((format (printf, 1, 2)));
|
||||||
|
|
||||||
|
std::string str_format(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
|
||||||
|
std::string result(800, ' ');
|
||||||
|
size_t n = vsnprintf(result.data(), result.size(), format, ap);
|
||||||
|
result.resize(n);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Convert string to unsigned integer. */
|
||||||
|
bool parse_uint(const std::string& s, unsigned int& v)
|
||||||
|
{
|
||||||
|
if (s.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t pos = 0;
|
||||||
|
unsigned long t = std::stoul(s, &pos, 10);
|
||||||
|
if (pos != s.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (t > UINT_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
v = t;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Convert string to floating point number. */
|
||||||
|
bool parse_float(const std::string& s, double& v)
|
||||||
|
{
|
||||||
|
if (s.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t pos = 0;
|
||||||
|
v = std::stod(s, &pos);
|
||||||
|
return (pos == s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Forward declaration.
|
// Forward declaration.
|
||||||
class ControlServer;
|
class ControlServer;
|
||||||
|
|
||||||
|
@ -60,14 +112,20 @@ class ControlServer;
|
||||||
class CommandHandler
|
class CommandHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// IDN response fields
|
// IDN response fields.
|
||||||
static constexpr std::string_view IDN_MANUFACTURER = "Jigsaw";
|
static constexpr const char * IDN_MANUFACTURER = "Jigsaw";
|
||||||
static constexpr std::string_view IDN_MODEL = "PuzzleFw";
|
static constexpr const char * IDN_MODEL = "PuzzleFw";
|
||||||
|
|
||||||
|
// Configuration files.
|
||||||
|
static constexpr const char * CFG_FILE_CALIBRATION =
|
||||||
|
"/var/lib/puzzlefw/cfg/calibration.conf";
|
||||||
|
static constexpr const char * CFG_FILE_NETWORK =
|
||||||
|
"/var/lib/puzzlefw/cfg/network.conf";
|
||||||
|
|
||||||
enum ExitStatus {
|
enum ExitStatus {
|
||||||
EXIT_ERROR = 1,
|
EXIT_ERROR = 1,
|
||||||
EXIT_HALT = 2,
|
EXIT_HALT = 10,
|
||||||
EXIT_REBOOT = 3
|
EXIT_REBOOT = 11
|
||||||
};
|
};
|
||||||
|
|
||||||
enum RangeSpec {
|
enum RangeSpec {
|
||||||
|
@ -76,6 +134,18 @@ public:
|
||||||
RANGE_HI = 2
|
RANGE_HI = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ChannelCalibration {
|
||||||
|
RangeSpec range_spec;
|
||||||
|
double offset_lo;
|
||||||
|
double offset_hi;
|
||||||
|
double gain_lo;
|
||||||
|
double gain_hi;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Calibration {
|
||||||
|
ChannelCalibration channel_cal[4];
|
||||||
|
};
|
||||||
|
|
||||||
struct CommandEnvironment {
|
struct CommandEnvironment {
|
||||||
int channel;
|
int channel;
|
||||||
RangeSpec range_spec;
|
RangeSpec range_spec;
|
||||||
|
@ -124,6 +194,34 @@ public:
|
||||||
return m_strand;
|
return m_strand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset non-persistent settings to power-on defaults.
|
||||||
|
*
|
||||||
|
* This resets all settings except for
|
||||||
|
* - saved calibration
|
||||||
|
* - active network configuration
|
||||||
|
* - saved network configuration.
|
||||||
|
*
|
||||||
|
* The calibration is reset to the saved calibration.
|
||||||
|
*/
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
read_calibration();
|
||||||
|
m_device.set_adc_simulation_enabled(false);
|
||||||
|
m_device.set_digital_simulation_enabled(false);
|
||||||
|
m_device.set_trigger_mode(TRIG_NONE);
|
||||||
|
m_device.set_trigger_ext_channel(0);
|
||||||
|
m_device.set_trigger_ext_falling(false);
|
||||||
|
m_device.set_trigger_delay(0);
|
||||||
|
m_device.set_4channel_mode(false);
|
||||||
|
m_device.set_decimation_factor(125);
|
||||||
|
m_device.set_averaging_enabled(true);
|
||||||
|
m_device.set_shift_steps(0);
|
||||||
|
m_device.set_record_length(1024);
|
||||||
|
m_device.set_acquisition_enabled(true);
|
||||||
|
m_device.set_timetagger_event_mask(0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a command.
|
* Handle a command.
|
||||||
*
|
*
|
||||||
|
@ -162,11 +260,11 @@ public:
|
||||||
&& action.compare(0, 6, "ain:ch") == 0
|
&& action.compare(0, 6, "ain:ch") == 0
|
||||||
&& action[7] == ':') {
|
&& action[7] == ':') {
|
||||||
char channel_digit = action[6];
|
char channel_digit = action[6];
|
||||||
if (channel_digit < '1' || channel_digit > '4') {
|
if (channel_digit < '0' || channel_digit > '3') {
|
||||||
return err_unknown_command();
|
return err_unknown_command();
|
||||||
}
|
}
|
||||||
env.channel = channel_digit - '1'; // 0-based channel index
|
env.channel = channel_digit - '0';
|
||||||
action[7] = 'N'; // mark channel index
|
action[6] = 'N'; // mark channel index
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract range specifier.
|
// Extract range specifier.
|
||||||
|
@ -239,7 +337,37 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// TODO -- shutdown flow
|
/** Asynchronously stop control and/or data servers. */
|
||||||
|
void stop_server(bool stop_control, std::function<void()> handler);
|
||||||
|
void stop_data_servers(unsigned int idx, std::function<void()> handler);
|
||||||
|
|
||||||
|
/** Asynchronously start control and/or data servers. */
|
||||||
|
void start_server(bool start_control);
|
||||||
|
void start_data_servers(unsigned int idx);
|
||||||
|
|
||||||
|
/** Read calibration from file. */
|
||||||
|
void read_calibration()
|
||||||
|
{
|
||||||
|
for (int c = 0; c < 4; c++) {
|
||||||
|
m_calibration.channel_cal[c].range_spec = RANGE_LO;
|
||||||
|
m_calibration.channel_cal[c].offset_lo = 8192;
|
||||||
|
m_calibration.channel_cal[c].offset_hi = 8192;
|
||||||
|
m_calibration.channel_cal[c].gain_lo = -8191;
|
||||||
|
m_calibration.channel_cal[c].gain_hi = -409;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - read from file
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convert a raw ADC sample to Volt. */
|
||||||
|
double convert_sample_to_volt(unsigned int channel, unsigned int sample)
|
||||||
|
{
|
||||||
|
ChannelCalibration& cal = m_calibration.channel_cal[channel];
|
||||||
|
RangeSpec range_spec = cal.range_spec;
|
||||||
|
double offs = (range_spec == RANGE_HI) ? cal.offset_hi : cal.offset_lo;
|
||||||
|
double gain = (range_spec == RANGE_HI) ? cal.gain_hi : cal.gain_lo;
|
||||||
|
return (sample - offs) / gain;
|
||||||
|
}
|
||||||
|
|
||||||
std::string err_unknown_command() const
|
std::string err_unknown_command() const
|
||||||
{
|
{
|
||||||
|
@ -256,6 +384,11 @@ private:
|
||||||
return "ERROR Missing argument";
|
return "ERROR Missing argument";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string err_invalid_argument() const
|
||||||
|
{
|
||||||
|
return "ERROR Invalid argument";
|
||||||
|
}
|
||||||
|
|
||||||
/** Parse command to a list of white space separated tokens. */
|
/** Parse command to a list of white space separated tokens. */
|
||||||
std::vector<std::string> parse_command(const std::string& command)
|
std::vector<std::string> parse_command(const std::string& command)
|
||||||
{
|
{
|
||||||
|
@ -285,154 +418,204 @@ private:
|
||||||
std::string qry_idn(CommandEnvironment env)
|
std::string qry_idn(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
VersionInfo fw_version = m_device.get_version_info();
|
VersionInfo fw_version = m_device.get_version_info();
|
||||||
std::stringstream idn;
|
return str_format("%s,%s,%s,FW-%d.%d/SW-%d.%d",
|
||||||
idn << IDN_MANUFACTURER;
|
IDN_MANUFACTURER,
|
||||||
idn << "," << IDN_MODEL;
|
IDN_MODEL,
|
||||||
idn << "," << m_serial_number;
|
m_serial_number.c_str(),
|
||||||
idn << ",";
|
fw_version.major_version,
|
||||||
idn << "FW-" << (int)fw_version.major_version;
|
fw_version.minor_version,
|
||||||
idn << "." << (int)fw_version.minor_version;
|
PUZZLEFW_SW_MAJOR,
|
||||||
idn << "/SW-" << PUZZLEFW_SW_MAJOR << "." << PUZZLEFW_SW_MINOR;
|
PUZZLEFW_SW_MINOR);
|
||||||
return idn.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command TIMESTAMP? */
|
/** Handle command TIMESTAMP? */
|
||||||
std::string qry_timestamp(CommandEnvironment env)
|
std::string qry_timestamp(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
uint64_t timestamp = m_device.get_timestamp();
|
||||||
return "ERROR";
|
return std::to_string(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:CHANNELS:COUNT? */
|
/** Handle command AIN:CHANNELS:COUNT? */
|
||||||
std::string qry_channels_count(CommandEnvironment env)
|
std::string qry_channels_count(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int n = m_device.get_analog_channel_count();
|
||||||
return "ERROR";
|
return std::to_string(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:CHANNELS:ACTIVE? */
|
/** Handle command AIN:CHANNELS:ACTIVE? */
|
||||||
std::string qry_channels_active(CommandEnvironment env)
|
std::string qry_channels_active(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
return m_device.is_4channel_mode() ? "4" : "2";
|
||||||
return "ERROR";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:CHn:RANGE? */
|
/** Handle command AIN:CHn:RANGE? */
|
||||||
std::string qry_channel_range(CommandEnvironment env)
|
std::string qry_channel_range(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
ChannelCalibration& cal = m_calibration.channel_cal[env.channel];
|
||||||
return "ERROR";
|
return (cal.range_spec == RANGE_HI) ? "HI" : "LO";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:CHn:OFFS[:range]? */
|
/** Handle command AIN:CHn:OFFS[:range]? */
|
||||||
std::string qry_channel_offs(CommandEnvironment env)
|
std::string qry_channel_offs(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
ChannelCalibration& cal = m_calibration.channel_cal[env.channel];
|
||||||
return "ERROR";
|
RangeSpec range_spec = env.range_spec;
|
||||||
|
if (range_spec == RANGE_NONE) {
|
||||||
|
range_spec = cal.range_spec;
|
||||||
|
}
|
||||||
|
double offs = (range_spec == RANGE_HI) ? cal.offset_hi : cal.offset_lo;
|
||||||
|
return str_format("%.6f", offs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:CHn:GAIN[:range]? */
|
/** Handle command AIN:CHn:GAIN[:range]? */
|
||||||
std::string qry_channel_gain(CommandEnvironment env)
|
std::string qry_channel_gain(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
ChannelCalibration& cal = m_calibration.channel_cal[env.channel];
|
||||||
return "ERROR";
|
RangeSpec range_spec = env.range_spec;
|
||||||
|
if (range_spec == RANGE_NONE) {
|
||||||
|
range_spec = cal.range_spec;
|
||||||
|
}
|
||||||
|
double gain = (range_spec == RANGE_HI) ? cal.gain_hi : cal.gain_lo;
|
||||||
|
return str_format("%.6f", gain);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:CHn:SAMPLE[:RAW]? */
|
/** Handle command AIN:CHn:SAMPLE[:RAW]? */
|
||||||
std::string qry_channel_sample(CommandEnvironment env)
|
std::string qry_channel_sample(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int sample = m_device.get_adc_sample(env.channel);
|
||||||
return "ERROR";
|
if (env.raw_flag) {
|
||||||
|
return std::to_string(sample);
|
||||||
|
} else {
|
||||||
|
double v = convert_sample_to_volt(env.channel, sample);
|
||||||
|
return str_format("%.6f", v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:CHn:MINMAX[:RAW]? */
|
/** Handle command AIN:CHn:MINMAX[:RAW]? */
|
||||||
std::string qry_channel_minmax(CommandEnvironment env)
|
std::string qry_channel_minmax(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int min_sample, max_sample;
|
||||||
return "ERROR";
|
m_device.get_adc_range(env.channel, min_sample, max_sample);
|
||||||
|
if (env.raw_flag) {
|
||||||
|
return std::to_string(min_sample) + " "
|
||||||
|
+ std::to_string(max_sample);
|
||||||
|
} else {
|
||||||
|
double vmin = convert_sample_to_volt(env.channel, min_sample);
|
||||||
|
double vmax = convert_sample_to_volt(env.channel, max_sample);
|
||||||
|
return str_format("%.6f %.6f", vmin, vmax);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:SRATE? */
|
/** Handle command AIN:SRATE? */
|
||||||
std::string qry_srate(CommandEnvironment env)
|
std::string qry_srate(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int divisor = m_device.get_decimation_factor();
|
||||||
return "ERROR";
|
double srate = 125e6 / divisor;
|
||||||
|
return str_format("%.3f", srate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:SRATE:DIVISOR? */
|
/** Handle command AIN:SRATE:DIVISOR? */
|
||||||
std::string qry_srate_divisor(CommandEnvironment env)
|
std::string qry_srate_divisor(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int divisor = m_device.get_decimation_factor();
|
||||||
return "ERROR";
|
return std::to_string(divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:SRATE:MODE? */
|
/** Handle command AIN:SRATE:MODE? */
|
||||||
std::string qry_srate_mode(CommandEnvironment env)
|
std::string qry_srate_mode(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
return m_device.is_averaging_enabled() ? "AVERAGE" : "DECIMATE";
|
||||||
return "ERROR";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:SRATE:GAIN? */
|
/** Handle command AIN:SRATE:GAIN? */
|
||||||
std::string qry_srate_gain(CommandEnvironment env)
|
std::string qry_srate_gain(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
double gain = 1.0;
|
||||||
return "ERROR";
|
if (m_device.is_averaging_enabled()) {
|
||||||
|
unsigned int divisor = m_device.get_decimation_factor();
|
||||||
|
int shift_steps = m_device.get_shift_steps();
|
||||||
|
gain = divisor / static_cast<double>(1 << shift_steps);
|
||||||
|
}
|
||||||
|
return str_format("%.8f", gain);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:NSAMPLES? */
|
/** Handle command AIN:NSAMPLES? */
|
||||||
std::string qry_nsamples(CommandEnvironment env)
|
std::string qry_nsamples(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int nsamples = m_device.get_record_length();
|
||||||
return "ERROR";
|
return std::to_string(nsamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:TRIGGER:MODE? */
|
/** Handle command AIN:TRIGGER:MODE? */
|
||||||
std::string qry_trigger_mode(CommandEnvironment env)
|
std::string qry_trigger_mode(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
TriggerMode trigger_mode = m_device.get_trigger_mode();
|
||||||
return "ERROR";
|
return trigger_mode_to_string(trigger_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handle command AIN:TRIGGER:EXT:CHANNEL? */
|
||||||
|
std::string qry_trigger_ext_channel(CommandEnvironment env)
|
||||||
|
{
|
||||||
|
unsigned int n = m_device.get_trigger_ext_channel();
|
||||||
|
return std::to_string(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handle command AIN:TRIGGER:EXT:EDGE? */
|
||||||
|
std::string qry_trigger_ext_edge(CommandEnvironment env)
|
||||||
|
{
|
||||||
|
return m_device.get_trigger_ext_falling() ? "FALLING" : "RISING";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:TRIGGER:DELAY? */
|
/** Handle command AIN:TRIGGER:DELAY? */
|
||||||
std::string qry_trigger_delay(CommandEnvironment env)
|
std::string qry_trigger_delay(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int n = m_device.get_trigger_delay();
|
||||||
return "ERROR";
|
return std::to_string(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:TRIGGER:STATUS? */
|
/** Handle command AIN:TRIGGER:STATUS? */
|
||||||
std::string qry_trigger_status(CommandEnvironment env)
|
std::string qry_trigger_status(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
if (m_device.is_waiting_for_trigger()) {
|
||||||
return "ERROR";
|
return "WAITING";
|
||||||
|
} else {
|
||||||
|
if (m_device.is_acquisition_enabled()) {
|
||||||
|
return "BUSY";
|
||||||
|
} else {
|
||||||
|
return "IDLE";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command TT:SAMPLE? */
|
/** Handle command TT:SAMPLE? */
|
||||||
std::string qry_tt_sample(CommandEnvironment env)
|
std::string qry_tt_sample(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int sample = m_device.get_digital_input_state();
|
||||||
return "ERROR";
|
return str_format("%d %d %d %d",
|
||||||
|
(sample & 1),
|
||||||
|
((sample >> 1) & 1),
|
||||||
|
((sample >> 2) & 1),
|
||||||
|
((sample >> 3) & 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command TT:CHANNEL:MASK? */
|
/** Handle command TT:EVENT:MASK? */
|
||||||
std::string qry_tt_channel_mask(CommandEnvironment env)
|
std::string qry_tt_event_mask(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int event_mask = m_device.get_timetagger_event_mask();
|
||||||
return "ERROR";
|
return std::to_string(event_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command IPCFG[:SAVED]? */
|
/** Handle command IPCFG[:SAVED]? */
|
||||||
std::string qry_ipcfg(CommandEnvironment env)
|
std::string qry_ipcfg(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO - read network config from file
|
||||||
return "ERROR";
|
return "ERROR";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command RESET */
|
/** Handle command RESET */
|
||||||
std::string cmd_reset(CommandEnvironment env)
|
std::string cmd_reset(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO -- reset
|
reset();
|
||||||
return "OK";
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,102 +650,320 @@ private:
|
||||||
/** Handle command AIN:MINMAX:CLEAR */
|
/** Handle command AIN:MINMAX:CLEAR */
|
||||||
std::string cmd_minmax_clear(CommandEnvironment env)
|
std::string cmd_minmax_clear(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
m_device.clear_adc_range();
|
||||||
return "ERROR";
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command TT:MARK */
|
/** Handle command TT:MARK */
|
||||||
std::string cmd_tt_mark(CommandEnvironment env)
|
std::string cmd_tt_mark(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
// TODO
|
m_device.timetagger_mark();
|
||||||
return "ERROR";
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:CHANNELS:ACTIVE */
|
/** Handle command AIN:CHANNELS:ACTIVE */
|
||||||
std::string cmd_channels_active(CommandEnvironment env,
|
std::string cmd_channels_active(CommandEnvironment env,
|
||||||
const std::string& arg)
|
const std::string& arg)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int n;
|
||||||
return "ERROR";
|
if (! parse_uint(arg, n)) {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
if (n != 2 && n != 4) {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce sample rate if necessary.
|
||||||
|
if (n == 4) {
|
||||||
|
unsigned int min_divisor = min_srate_divisor(
|
||||||
|
m_device.get_trigger_mode() == TRIG_AUTO,
|
||||||
|
true);
|
||||||
|
unsigned int divisor = m_device.get_decimation_factor();
|
||||||
|
if (divisor < min_divisor) {
|
||||||
|
m_device.set_decimation_factor(min_divisor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_device.set_4channel_mode(n == 4);
|
||||||
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:CHn:RANGE */
|
/** Handle command AIN:CHn:RANGE */
|
||||||
std::string cmd_channel_range(CommandEnvironment env,
|
std::string cmd_channel_range(CommandEnvironment env,
|
||||||
const std::string& arg)
|
const std::string& arg)
|
||||||
{
|
{
|
||||||
// TODO
|
std::string range_name = str_to_lower(arg);
|
||||||
return "ERROR";
|
|
||||||
|
if (range_name == "lo") {
|
||||||
|
m_calibration.channel_cal[env.channel].range_spec = RANGE_LO;
|
||||||
|
} else if (range_name == "hi") {
|
||||||
|
m_calibration.channel_cal[env.channel].range_spec = RANGE_HI;
|
||||||
|
} else {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:CHn:OFFS[:range] */
|
/** Handle command AIN:CHn:OFFS[:range] */
|
||||||
std::string cmd_channel_offs(CommandEnvironment env,
|
std::string cmd_channel_offs(CommandEnvironment env,
|
||||||
const std::string& arg)
|
const std::string& arg)
|
||||||
{
|
{
|
||||||
// TODO
|
double offs;
|
||||||
return "ERROR";
|
if ((! parse_float(arg, offs))
|
||||||
|
|| (offs < 0)
|
||||||
|
|| (offs > 16383)) {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelCalibration& cal = m_calibration.channel_cal[env.channel];
|
||||||
|
RangeSpec range_spec = env.range_spec;
|
||||||
|
if (range_spec == RANGE_NONE) {
|
||||||
|
range_spec = cal.range_spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (range_spec == RANGE_LO) {
|
||||||
|
cal.offset_lo = offs;
|
||||||
|
}
|
||||||
|
if (range_spec == RANGE_HI) {
|
||||||
|
cal.offset_hi = offs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:CHn:GAIN[:range] */
|
/** Handle command AIN:CHn:GAIN[:range] */
|
||||||
std::string cmd_channel_gain(CommandEnvironment env,
|
std::string cmd_channel_gain(CommandEnvironment env,
|
||||||
const std::string& arg)
|
const std::string& arg)
|
||||||
{
|
{
|
||||||
// TODO
|
double gain;
|
||||||
return "ERROR";
|
if ((! parse_float(arg, gain))
|
||||||
|
|| (gain < -1e6)
|
||||||
|
|| (gain > 1e6)) {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelCalibration& cal = m_calibration.channel_cal[env.channel];
|
||||||
|
RangeSpec range_spec = env.range_spec;
|
||||||
|
if (range_spec == RANGE_NONE) {
|
||||||
|
range_spec = cal.range_spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (range_spec == RANGE_LO) {
|
||||||
|
cal.gain_lo = gain;
|
||||||
|
}
|
||||||
|
if (range_spec == RANGE_HI) {
|
||||||
|
cal.gain_hi = gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return minimum sample rate divisor depending on trigger mode
|
||||||
|
* and number of active channels.
|
||||||
|
*
|
||||||
|
* In auto-trigger mode, divisor must be at least 2.
|
||||||
|
* In 4-channel mode, divisor must be at least 2 or 4 depending
|
||||||
|
* on auto-trigger mode.
|
||||||
|
*/
|
||||||
|
unsigned int min_srate_divisor(bool trig_auto, bool ch4)
|
||||||
|
{
|
||||||
|
unsigned int min_divisor = 1;
|
||||||
|
if (trig_auto) {
|
||||||
|
min_divisor *= 2;
|
||||||
|
}
|
||||||
|
if (ch4) {
|
||||||
|
min_divisor += 2;
|
||||||
|
}
|
||||||
|
return min_divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Commond handling for setting sample rate. */
|
||||||
|
std::string set_srate_divisor(unsigned int divisor)
|
||||||
|
{
|
||||||
|
unsigned int min_divisor = min_srate_divisor(
|
||||||
|
m_device.get_trigger_mode() == TRIG_AUTO,
|
||||||
|
m_device.is_4channel_mode());
|
||||||
|
|
||||||
|
if ((divisor < min_divisor)
|
||||||
|
|| (divisor > PuzzleFwDevice::MAX_DECIMATION_FACTOR)) {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_device.set_decimation_factor(divisor);
|
||||||
|
|
||||||
|
if (m_device.is_averaging_enabled()) {
|
||||||
|
// Adjust shift steps to avoid 24-bit overflow.
|
||||||
|
int shift_steps = 0;
|
||||||
|
while (divisor > (1UL << (10 + shift_steps))) {
|
||||||
|
shift_steps++;
|
||||||
|
}
|
||||||
|
m_device.set_shift_steps(shift_steps);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:SRATE */
|
/** Handle command AIN:SRATE */
|
||||||
std::string cmd_srate(CommandEnvironment env,
|
std::string cmd_srate(CommandEnvironment env,
|
||||||
const std::string& arg)
|
const std::string& arg)
|
||||||
{
|
{
|
||||||
// TODO
|
double v;
|
||||||
return "ERROR";
|
if ((! parse_float(arg, v)) || (v < 1) || (v > 125e6)) {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int divisor = lrint(125e6 / v);
|
||||||
|
return set_srate_divisor(divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:SRATE:DIVISOR */
|
/** Handle command AIN:SRATE:DIVISOR */
|
||||||
std::string cmd_srate_divisor(CommandEnvironment env,
|
std::string cmd_srate_divisor(CommandEnvironment env,
|
||||||
const std::string& arg)
|
const std::string& arg)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int divisor;
|
||||||
return "ERROR";
|
if (! parse_uint(arg, divisor)) {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
|
||||||
|
return set_srate_divisor(divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:SRATE:MODE */
|
/** Handle command AIN:SRATE:MODE */
|
||||||
std::string cmd_srate_mode(CommandEnvironment env,
|
std::string cmd_srate_mode(CommandEnvironment env,
|
||||||
const std::string& arg)
|
const std::string& arg)
|
||||||
{
|
{
|
||||||
// TODO
|
std::string srate_mode = str_to_lower(arg);
|
||||||
return "ERROR";
|
|
||||||
|
if (srate_mode == "average") {
|
||||||
|
|
||||||
|
// Adjust shift steps to avoid 24-bit overflow.
|
||||||
|
unsigned int divisor = m_device.get_decimation_factor();
|
||||||
|
int shift_steps = 0;
|
||||||
|
while (divisor > (1UL << (10 + shift_steps))) {
|
||||||
|
shift_steps++;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_device.set_averaging_enabled(true);
|
||||||
|
m_device.set_shift_steps(shift_steps);
|
||||||
|
} else if (srate_mode == "decimate") {
|
||||||
|
m_device.set_averaging_enabled(false);
|
||||||
|
m_device.set_shift_steps(0);
|
||||||
|
} else {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:NSAMPLES */
|
/** Handle command AIN:NSAMPLES */
|
||||||
std::string cmd_nsamples(CommandEnvironment env,
|
std::string cmd_nsamples(CommandEnvironment env,
|
||||||
const std::string& arg)
|
const std::string& arg)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int n;
|
||||||
return "ERROR";
|
if ((! parse_uint(arg, n))
|
||||||
|
|| (n < 1)
|
||||||
|
|| (n > PuzzleFwDevice::MAX_RECORD_LENGTH)) {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
m_device.set_record_length(n);
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handle command AIN:TRIGGER */
|
||||||
|
std::string cmd_trigger(CommandEnvironment env)
|
||||||
|
{
|
||||||
|
m_device.trigger_force();
|
||||||
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:TRIGGER:MODE */
|
/** Handle command AIN:TRIGGER:MODE */
|
||||||
std::string cmd_trigger_mode(CommandEnvironment env,
|
std::string cmd_trigger_mode(CommandEnvironment env,
|
||||||
const std::string& arg)
|
const std::string& arg)
|
||||||
{
|
{
|
||||||
// TODO
|
std::string trigger_mode = str_to_lower(arg);
|
||||||
return "ERROR";
|
|
||||||
|
if (trigger_mode == "none") {
|
||||||
|
m_device.set_trigger_mode(TRIG_NONE);
|
||||||
|
} else if (trigger_mode == "auto") {
|
||||||
|
// Reduce sample rate if necessary.
|
||||||
|
unsigned int min_divisor = min_srate_divisor(
|
||||||
|
true,
|
||||||
|
m_device.is_4channel_mode());
|
||||||
|
unsigned int divisor = m_device.get_decimation_factor();
|
||||||
|
if (divisor < min_divisor) {
|
||||||
|
m_device.set_decimation_factor(min_divisor);
|
||||||
|
}
|
||||||
|
m_device.set_trigger_mode(TRIG_AUTO);
|
||||||
|
} else if (trigger_mode == "external") {
|
||||||
|
m_device.set_trigger_mode(TRIG_EXTERNAL);
|
||||||
|
} else if (trigger_mode == "external_once") {
|
||||||
|
m_device.set_trigger_mode(TRIG_EXTERNAL_ONCE);
|
||||||
|
} else {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handle command AIN:TRIGGER:EXT:CHANNEL */
|
||||||
|
std::string cmd_trigger_ext_channel(CommandEnvironment env,
|
||||||
|
const std::string& arg)
|
||||||
|
{
|
||||||
|
unsigned int n;
|
||||||
|
if ((! parse_uint(arg, n)) || (n > 3)) {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
m_device.set_trigger_ext_channel(n);
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handle command AIN:TRIGGER:EXT:EDGE */
|
||||||
|
std::string cmd_trigger_ext_edge(CommandEnvironment env,
|
||||||
|
const std::string& arg)
|
||||||
|
{
|
||||||
|
std::string edge = str_to_lower(arg);
|
||||||
|
if (edge == "rising") {
|
||||||
|
m_device.set_trigger_ext_falling(false);
|
||||||
|
} else if (edge == "falling") {
|
||||||
|
m_device.set_trigger_ext_falling(true);
|
||||||
|
} else {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command AIN:TRIGGER:DELAY */
|
/** Handle command AIN:TRIGGER:DELAY */
|
||||||
std::string cmd_trigger_delay(CommandEnvironment env,
|
std::string cmd_trigger_delay(CommandEnvironment env,
|
||||||
const std::string& arg)
|
const std::string& arg)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int n;
|
||||||
return "ERROR";
|
if ((! parse_uint(arg, n))
|
||||||
|
|| (n > PuzzleFwDevice::MAX_TRIGGER_DELAY)) {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
m_device.set_trigger_delay(n);
|
||||||
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command TT:CHANNEL:MASK */
|
/** Handle command TT:EVENT:MASK */
|
||||||
std::string cmd_tt_channel_mask(CommandEnvironment env,
|
std::string cmd_tt_event_mask(CommandEnvironment env,
|
||||||
const std::string& arg)
|
const std::string& arg)
|
||||||
{
|
{
|
||||||
// TODO
|
unsigned int n;
|
||||||
|
if ((! parse_uint(arg, n)) || (n > 255)) {
|
||||||
|
return err_invalid_argument();
|
||||||
|
}
|
||||||
|
m_device.set_timetagger_event_mask(n);
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handle command AIN:MINMAX:CLEAR */
|
||||||
|
std::string cmd_cal_save(CommandEnvironment env)
|
||||||
|
{
|
||||||
|
// TODO - write calibration to file
|
||||||
|
// TODO - invoke script to save to SD card
|
||||||
return "ERROR";
|
return "ERROR";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,8 +973,9 @@ private:
|
||||||
const std::string& netmask,
|
const std::string& netmask,
|
||||||
const std::string& gateway)
|
const std::string& gateway)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO -- parse and check address
|
||||||
return "OK";
|
// TODO -- activate or save address, restart networking if needed
|
||||||
|
return "ERROR";
|
||||||
}
|
}
|
||||||
|
|
||||||
static const inline std::map<
|
static const inline std::map<
|
||||||
|
@ -591,24 +993,29 @@ private:
|
||||||
{ "ain:chN:gain:RR?", &CommandHandler::qry_channel_gain },
|
{ "ain:chN:gain:RR?", &CommandHandler::qry_channel_gain },
|
||||||
{ "ain:chN:sample?", &CommandHandler::qry_channel_sample },
|
{ "ain:chN:sample?", &CommandHandler::qry_channel_sample },
|
||||||
{ "ain:chN:sample:raw?", &CommandHandler::qry_channel_sample },
|
{ "ain:chN:sample:raw?", &CommandHandler::qry_channel_sample },
|
||||||
{ "ain:chN:minman?", &CommandHandler::qry_channel_minmax },
|
{ "ain:chN:minmax?", &CommandHandler::qry_channel_minmax },
|
||||||
{ "ain:chN:manmax:raw?", &CommandHandler::qry_channel_minmax },
|
{ "ain:chN:minmax:raw?", &CommandHandler::qry_channel_minmax },
|
||||||
{ "ain:srate?", &CommandHandler::qry_srate },
|
{ "ain:srate?", &CommandHandler::qry_srate },
|
||||||
{ "ain:srate:divisor?", &CommandHandler::qry_srate_divisor },
|
{ "ain:srate:divisor?", &CommandHandler::qry_srate_divisor },
|
||||||
{ "ain:srate:mode?", &CommandHandler::qry_srate_mode },
|
{ "ain:srate:mode?", &CommandHandler::qry_srate_mode },
|
||||||
{ "ain:srate:gain?", &CommandHandler::qry_srate_gain },
|
{ "ain:srate:gain?", &CommandHandler::qry_srate_gain },
|
||||||
{ "ain:nsamples?", &CommandHandler::qry_nsamples },
|
{ "ain:nsamples?", &CommandHandler::qry_nsamples },
|
||||||
{ "ain:trigger:mode?", &CommandHandler::qry_trigger_mode },
|
{ "ain:trigger:mode?", &CommandHandler::qry_trigger_mode },
|
||||||
|
{ "ain:trigger:ext:channel?",
|
||||||
|
&CommandHandler::qry_trigger_ext_channel },
|
||||||
|
{ "ain:trigger:ext:edge?", &CommandHandler::qry_trigger_ext_edge },
|
||||||
{ "ain:trigger:delay?", &CommandHandler::qry_trigger_delay },
|
{ "ain:trigger:delay?", &CommandHandler::qry_trigger_delay },
|
||||||
{ "ain:trigger:status?", &CommandHandler::qry_trigger_status },
|
{ "ain:trigger:status?", &CommandHandler::qry_trigger_status },
|
||||||
{ "tt:sample?", &CommandHandler::qry_tt_sample },
|
{ "tt:sample?", &CommandHandler::qry_tt_sample },
|
||||||
{ "tt:channel:mask?", &CommandHandler::qry_tt_channel_mask },
|
{ "tt:event:mask?", &CommandHandler::qry_tt_event_mask },
|
||||||
{ "ipcfg?", &CommandHandler::qry_ipcfg },
|
{ "ipcfg?", &CommandHandler::qry_ipcfg },
|
||||||
{ "ipcfg:saved?", &CommandHandler::qry_ipcfg },
|
{ "ipcfg:saved?", &CommandHandler::qry_ipcfg },
|
||||||
{ "reset", &CommandHandler::cmd_reset },
|
{ "reset", &CommandHandler::cmd_reset },
|
||||||
{ "halt", &CommandHandler::cmd_halt },
|
{ "halt", &CommandHandler::cmd_halt },
|
||||||
{ "reboot", &CommandHandler::cmd_reboot },
|
{ "reboot", &CommandHandler::cmd_reboot },
|
||||||
|
{ "ain:cal:save", &CommandHandler::cmd_cal_save },
|
||||||
{ "ain:minmax:clear", &CommandHandler::cmd_minmax_clear },
|
{ "ain:minmax:clear", &CommandHandler::cmd_minmax_clear },
|
||||||
|
{ "ain:trigger", &CommandHandler::cmd_trigger },
|
||||||
{ "tt:mark", &CommandHandler::cmd_tt_mark }
|
{ "tt:mark", &CommandHandler::cmd_tt_mark }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -627,8 +1034,10 @@ private:
|
||||||
{ "ain:srate:mode", &CommandHandler::cmd_srate_mode },
|
{ "ain:srate:mode", &CommandHandler::cmd_srate_mode },
|
||||||
{ "ain:nsamples", &CommandHandler::cmd_nsamples },
|
{ "ain:nsamples", &CommandHandler::cmd_nsamples },
|
||||||
{ "ain:trigger:mode", &CommandHandler::cmd_trigger_mode },
|
{ "ain:trigger:mode", &CommandHandler::cmd_trigger_mode },
|
||||||
|
{ "ain:trigger:ext:channel", &CommandHandler::cmd_trigger_ext_channel },
|
||||||
|
{ "ain:trigger:ext:edge", &CommandHandler::cmd_trigger_ext_edge },
|
||||||
{ "ain:trigger:delay", &CommandHandler::cmd_trigger_delay },
|
{ "ain:trigger:delay", &CommandHandler::cmd_trigger_delay },
|
||||||
{ "tt:channel:mask", &CommandHandler::cmd_tt_channel_mask }
|
{ "tt:event:mask", &CommandHandler::cmd_tt_event_mask }
|
||||||
};
|
};
|
||||||
|
|
||||||
asio::io_context& m_io;
|
asio::io_context& m_io;
|
||||||
|
@ -637,6 +1046,7 @@ private:
|
||||||
std::string m_serial_number;
|
std::string m_serial_number;
|
||||||
ControlServer* m_control_server;
|
ControlServer* m_control_server;
|
||||||
std::vector<DataServer*> m_data_servers;
|
std::vector<DataServer*> m_data_servers;
|
||||||
|
Calibration m_calibration;
|
||||||
bool m_shutting_down;
|
bool m_shutting_down;
|
||||||
ExitStatus m_exit_status;
|
ExitStatus m_exit_status;
|
||||||
};
|
};
|
||||||
|
@ -663,6 +1073,12 @@ public:
|
||||||
ControlServer(const ControlServer&) = delete;
|
ControlServer(const ControlServer&) = delete;
|
||||||
ControlServer& operator=(const ControlServer&) = delete;
|
ControlServer& operator=(const ControlServer&) = delete;
|
||||||
|
|
||||||
|
/** Return the Asio strand that runs all handlers for this object. */
|
||||||
|
asio::strand<asio::io_context::executor_type> get_executor()
|
||||||
|
{
|
||||||
|
return m_strand;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the server.
|
* Start the server.
|
||||||
*
|
*
|
||||||
|
@ -956,6 +1372,75 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ******** Methods for class CommandHandler ******** */
|
||||||
|
|
||||||
|
void CommandHandler::stop_server(bool stop_control,
|
||||||
|
std::function<void()> handler)
|
||||||
|
{
|
||||||
|
if (stop_control) {
|
||||||
|
asio::post(m_control_server->get_executor(),
|
||||||
|
[this,handler]() {
|
||||||
|
m_control_server->stop_server();
|
||||||
|
asio::post(m_strand,
|
||||||
|
[this,handler]() {
|
||||||
|
stop_data_servers(0, handler);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
stop_data_servers(0, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandHandler::stop_data_servers(unsigned int idx,
|
||||||
|
std::function<void()> handler)
|
||||||
|
{
|
||||||
|
if (idx < m_data_servers.size()) {
|
||||||
|
asio::post(m_data_servers[idx]->get_executor(),
|
||||||
|
[this,idx,handler]() {
|
||||||
|
m_data_servers[idx]->stop_server();
|
||||||
|
asio::post(m_strand,
|
||||||
|
[this,idx,handler]() {
|
||||||
|
stop_data_servers(idx + 1, handler);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandHandler::start_server(bool start_control)
|
||||||
|
{
|
||||||
|
if (start_control) {
|
||||||
|
asio::post(m_control_server->get_executor(),
|
||||||
|
[this]() {
|
||||||
|
m_control_server->start_server();
|
||||||
|
asio::post(m_strand,
|
||||||
|
[this]() {
|
||||||
|
start_data_servers(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
start_data_servers(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandHandler::start_data_servers(unsigned int idx)
|
||||||
|
{
|
||||||
|
if (idx < m_data_servers.size()) {
|
||||||
|
asio::post(m_data_servers[idx]->get_executor(),
|
||||||
|
[this,idx]() {
|
||||||
|
m_data_servers[idx]->start_server();
|
||||||
|
asio::post(m_strand,
|
||||||
|
[this,idx]() {
|
||||||
|
start_data_servers(idx + 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ******** Main program ******** */
|
||||||
|
|
||||||
/** Run remote control server. */
|
/** Run remote control server. */
|
||||||
int run_remote_control_server(
|
int run_remote_control_server(
|
||||||
puzzlefw::PuzzleFwDevice& device,
|
puzzlefw::PuzzleFwDevice& device,
|
||||||
|
@ -1011,13 +1496,19 @@ int run_remote_control_server(
|
||||||
command_handler.add_data_server(acq_server);
|
command_handler.add_data_server(acq_server);
|
||||||
command_handler.add_data_server(timetagger_server);
|
command_handler.add_data_server(timetagger_server);
|
||||||
|
|
||||||
// Disable DMA engine on exit from this function.
|
// Restore firmware status on exit from this function.
|
||||||
struct ScopeGuard {
|
struct ScopeGuard {
|
||||||
PuzzleFwDevice& m_device;
|
PuzzleFwDevice& m_device;
|
||||||
ScopeGuard(PuzzleFwDevice& device) : m_device(device) { }
|
ScopeGuard(PuzzleFwDevice& device) : m_device(device) { }
|
||||||
~ScopeGuard() { m_device.set_dma_enabled(false); }
|
~ScopeGuard() {
|
||||||
|
m_device.set_dma_enabled(false);
|
||||||
|
m_device.set_acquisition_enabled(false);
|
||||||
|
}
|
||||||
} scope_guard(device);
|
} scope_guard(device);
|
||||||
|
|
||||||
|
// Reset instrument.
|
||||||
|
command_handler.reset();
|
||||||
|
|
||||||
// Clear DMA errors, then enable DMA engine.
|
// Clear DMA errors, then enable DMA engine.
|
||||||
device.clear_dma_errors();
|
device.clear_dma_errors();
|
||||||
device.set_dma_enabled(true);
|
device.set_dma_enabled(true);
|
||||||
|
|
Loading…
Reference in New Issue