Compare commits

..

No commits in common. "2ab072b254ddd4f768071d9a536136540627b9cd" and "44cfd36a45e8a6d24cbcef9a519aa470b7be47f7" have entirely different histories.

5 changed files with 44 additions and 156 deletions

View File

@ -351,11 +351,9 @@ In the response string, such data elements are separated by space characters.
| `AIN:TRIGGER:EXT:CHANNEL` | External trigger channel. | | `AIN:TRIGGER:EXT:CHANNEL` | External trigger channel. |
| `AIN:TRIGGER:EXT:EDGE` | External trigger edge. | | `AIN:TRIGGER:EXT:EDGE` | External trigger edge. |
| `AIN:ACQUIRE:ENABLE` | Enable analog acquisition. | | `AIN:ACQUIRE:ENABLE` | Enable analog acquisition. |
| `AIN:CLEAR` | Clear buffer and drop data connection. |
| `TT:SAMPLE?` | Digital input state. | | `TT:SAMPLE?` | Digital input state. |
| `TT:EVENT:MASK` | Timetagger event mask. | | `TT:EVENT:MASK` | Timetagger event mask. |
| `TT:MARK` | Emit timetagger marker. | | `TT:MARK` | Emit timetagger marker. |
| `TT:CLEAR` | Clear buffer and drop data connection. |
| `TEMP:FPGA?` | FPGA temperature. | | `TEMP:FPGA?` | FPGA temperature. |
| `IPCFG[:SAVED]` | IP address configuration. | | `IPCFG[:SAVED]` | IP address configuration. |
| `HALT` | Shut down system. | | `HALT` | Shut down system. |
@ -628,17 +626,6 @@ When disabled, all triggers are ignored and any ongoing analog acquisition stops
Query: `AIN:ACQUIRE:ENABLE?` <br> Query: `AIN:ACQUIRE:ENABLE?` <br>
Response: either `0` or `1`. Response: either `0` or `1`.
### `AIN:CLEAR`
Command: `AIN:CLEAR`
This command clears internal buffers holding analog acquisition data.
It also drops the current data connection to port 5001, if any.
Clients should send this command just before connecting to the analog
sample data port and enabling analog data acquisition.
This prevents the transmission of stale data from a previous run.
### `TT:SAMPLE?` ### `TT:SAMPLE?`
Query: `TT:SAMPLE?` <br> Query: `TT:SAMPLE?` <br>
@ -675,17 +662,6 @@ Command: `TT:MARK`
This command emits a marker record in the timetagger event stream. This command emits a marker record in the timetagger event stream.
### `TT:CLEAR`
Command: `TT:CLEAR`
This command clears internal buffers holding timetagger data.
It also drops the current data connection to port 5002, if any.
Clients should send this command just before connecting to the timetagger
data port and enabling timetagger events.
This prevents the transmission of stale data from a previous run.
### `TEMP:FPGA?` ### `TEMP:FPGA?`
Query: `TEMP:FPGA?` <br> Query: `TEMP:FPGA?` <br>

View File

@ -115,12 +115,6 @@ public:
asio::ip::tcp::endpoint addr(asio::ip::address_v6::any(), m_tcp_port); asio::ip::tcp::endpoint addr(asio::ip::address_v6::any(), m_tcp_port);
m_acceptor.bind(addr); m_acceptor.bind(addr);
// Clear buffer, then enable DMA stream.
if (! m_connection.is_open()) {
discard_stale_data();
m_dma_stream.set_enabled(true);
}
// Start listening for connections. // Start listening for connections.
m_acceptor.listen(); m_acceptor.listen();
@ -139,6 +133,9 @@ public:
*/ */
void stop_server() void stop_server()
{ {
// Disable DMA stream and clear buffer.
m_dma_stream.init();
// Stop accepting connections. // Stop accepting connections.
if (m_acceptor.is_open()) { if (m_acceptor.is_open()) {
log(LOG_INFO, "Closing TCP server on port %d", m_tcp_port); log(LOG_INFO, "Closing TCP server on port %d", m_tcp_port);
@ -152,34 +149,6 @@ public:
m_stale_receive = true; m_stale_receive = true;
m_stale_send = m_send_in_progress; m_stale_send = m_send_in_progress;
} }
// Disable DMA stream and clear buffer.
m_dma_stream.init();
}
/**
* Disconnect the current client and discard all pending data.
*
* If a multi-threaded Asio event loop is running, this method may only
* be called through the strand returned by "get_executor()".
*/
void clear_data()
{
// Close the current connection.
if (m_connection.is_open()) {
log(LOG_INFO, "Closing connection to port %d", m_tcp_port);
m_connection.close();
m_stale_receive = true;
m_stale_send = m_send_in_progress;
}
// Disable DMA stream and clear buffer.
discard_stale_data();
// Re-enable DMA stream.
if (m_acceptor.is_open()) {
m_dma_stream.set_enabled(true);
}
} }
/** /**
@ -303,6 +272,10 @@ private:
[this](auto ec, size_t n){ handle_receive(ec, n); }); [this](auto ec, size_t n){ handle_receive(ec, n); });
} }
// Clear buffer, then enable DMA stream.
discard_stale_data();
m_dma_stream.set_enabled(true);
// Prepare to send data. // Prepare to send data.
transmit_data(false); transmit_data(false);
} }

View File

@ -732,6 +732,7 @@ public:
// Set up DMA buffer segment. // Set up DMA buffer segment.
m_device.write_reg(m_reg.addr_start, m_buf_start); m_device.write_reg(m_reg.addr_start, m_buf_start);
m_device.write_reg(m_reg.addr_end, m_buf_end); m_device.write_reg(m_reg.addr_end, m_buf_end);
m_device.write_reg(m_reg.addr_limit, m_buf_end - POINTER_MARGIN);
// Initialize DMA stream and reset write pointer. // Initialize DMA stream and reset write pointer.
init(); init();
@ -790,9 +791,6 @@ public:
// Reset read pointer. // Reset read pointer.
m_read_pointer = m_buf_start; m_read_pointer = m_buf_start;
// Reset write limit pointer.
m_device.write_reg(m_reg.addr_limit, m_buf_end - POINTER_MARGIN);
} }
/** Return the number of bytes available in the DMA buffer. */ /** Return the number of bytes available in the DMA buffer. */

View File

@ -481,8 +481,6 @@ public:
, m_model_name(model_name) , m_model_name(model_name)
, m_serial_number(serial_number) , m_serial_number(serial_number)
, m_control_server(nullptr) , m_control_server(nullptr)
, m_acquisition_server(nullptr)
, m_timetagger_server(nullptr)
, m_shutting_down(false) , m_shutting_down(false)
, m_exit_status(EXIT_ERROR) , m_exit_status(EXIT_ERROR)
{ } { }
@ -497,13 +495,10 @@ public:
m_control_server = &control_server; m_control_server = &control_server;
} }
/** Register the data servers with the command handler. */ /** Register a data server with the command handler. */
void set_data_servers(DataServer& acquisition_server, void add_data_server(DataServer& data_server)
DataServer& timetagger_server)
{ {
m_acquisition_server = &acquisition_server; m_data_servers.push_back(&data_server);
m_timetagger_server = &timetagger_server;
m_data_servers = {m_acquisition_server, m_timetagger_server};
} }
/** Return the exit status of the command handler. */ /** Return the exit status of the command handler. */
@ -556,20 +551,17 @@ public:
* If a multi-threaded Asio event loop is running, this method may only * If a multi-threaded Asio event loop is running, this method may only
* be called through the strand returned by "get_executor()". * be called through the strand returned by "get_executor()".
* *
* The specified response function will be called exactly once to pass * Returns:
* the command response (without line terminator). An empty string is * Response without line terminator,
* passed if no response must be sent. * or an empty string if no response must be sent.
*
* The call to the response function may be deferred.
*/ */
void handle_command(const std::string& command, std::string handle_command(const std::string& command)
std::function<void(std::string)> respond)
{ {
if (m_shutting_down) { if (m_shutting_down) {
// The server is shutting down. // The server is shutting down.
// Ignore new commands and send no response. // Ignore new commands and return an empty string to indicate
respond(std::string()); // that no response must be sent.
return; return std::string();
} }
// Split words. // Split words.
@ -577,8 +569,7 @@ public:
// Ignore empty command line without response. // Ignore empty command line without response.
if (tokens.empty()) { if (tokens.empty()) {
respond(std::string()); return std::string();
return;
} }
// Convert command to lower case. // Convert command to lower case.
@ -593,8 +584,7 @@ public:
&& action[7] == ':') { && action[7] == ':') {
char channel_digit = action[6]; char channel_digit = action[6];
if (channel_digit < '1' || channel_digit > '4') { if (channel_digit < '1' || channel_digit > '4') {
respond(err_unknown_command()); return err_unknown_command();
return;
} }
env.channel = channel_digit - '1'; env.channel = channel_digit - '1';
action[6] = 'N'; // mark channel index action[6] = 'N'; // mark channel index
@ -632,13 +622,10 @@ public:
const auto it = command_table_no_args.find(action); const auto it = command_table_no_args.find(action);
if (it != command_table_no_args.end()) { if (it != command_table_no_args.end()) {
if (tokens.size() != 1) { if (tokens.size() != 1) {
respond(err_unexpected_argument()); return err_unexpected_argument();
return;
} }
auto func = it->second; auto func = it->second;
std::string resp = (this->*func)(env); return (this->*func)(env);
respond(resp);
return;
} }
} }
@ -647,57 +634,37 @@ public:
const auto it = command_table_one_arg.find(action); const auto it = command_table_one_arg.find(action);
if (it != command_table_one_arg.end()) { if (it != command_table_one_arg.end()) {
if (tokens.size() < 2) { if (tokens.size() < 2) {
respond(err_missing_argument()); return err_missing_argument();
return;
} }
if (tokens.size() > 2) { if (tokens.size() > 2) {
respond(err_unexpected_argument()); return err_unexpected_argument();
return;
} }
auto func = it->second; auto func = it->second;
std::string resp = (this->*func)(env, tokens[1]); return (this->*func)(env, tokens[1]);
respond(resp);
return;
} }
} }
// Handle command IPCFG. // Handle command IPCFG.
if (action == "ipcfg" || action == "ipcfg:saved") { if (action == "ipcfg" || action == "ipcfg:saved") {
if (tokens.size() < 2) { if (tokens.size() < 2) {
respond(err_missing_argument()); return err_missing_argument();
return;
} }
if (tokens.size() > 5) { if (tokens.size() > 5) {
respond(err_unexpected_argument()); return err_unexpected_argument();
return;
} }
tokens.erase(tokens.begin()); tokens.erase(tokens.begin());
std::string resp = cmd_ipcfg(env, tokens); return cmd_ipcfg(env, tokens);
respond(resp);
return;
} }
// Handle command AIN:CLEAR. return err_unknown_command();
if (action == "ain:clear") {
cmd_ain_clear(respond);
return;
}
// Handle command TT:CLEAR.
if (action == "tt:clear") {
cmd_tt_clear(respond);
return;
}
respond(err_unknown_command());
} }
private: private:
/** Asynchronously stop control and data servers. */ /** Asynchronously stop control and/or data servers. */
void stop_server(std::function<void()> handler); void stop_server(std::function<void()> handler);
void stop_data_servers(unsigned int idx, std::function<void()> handler); void stop_data_servers(unsigned int idx, std::function<void()> handler);
/** Asynchronously start control and data servers. */ /** Asynchronously start control and/or data servers. */
void start_server(); void start_server();
void start_data_servers(unsigned int idx); void start_data_servers(unsigned int idx);
@ -1398,26 +1365,6 @@ private:
} }
} }
/** Handle command AIN:CLEAR */
void cmd_ain_clear(std::function<void(std::string)> respond)
{
asio::post(m_acquisition_server->get_executor(),
[this,respond]() {
m_acquisition_server->clear_data();
respond("OK");
});
}
/** Handle command TT:CLEAR */
void cmd_tt_clear(std::function<void(std::string)> respond)
{
asio::post(m_timetagger_server->get_executor(),
[this,respond]() {
m_timetagger_server->clear_data();
respond("OK");
});
}
static const inline std::map< static const inline std::map<
std::string, std::string,
std::string (CommandHandler::*)(CommandEnvironment)> std::string (CommandHandler::*)(CommandEnvironment)>
@ -1489,8 +1436,6 @@ private:
std::string m_model_name; std::string m_model_name;
std::string m_serial_number; std::string m_serial_number;
ControlServer* m_control_server; ControlServer* m_control_server;
DataServer* m_acquisition_server;
DataServer* m_timetagger_server;
std::vector<DataServer*> m_data_servers; std::vector<DataServer*> m_data_servers;
Calibration m_calibration; Calibration m_calibration;
bool m_shutting_down; bool m_shutting_down;
@ -1770,22 +1715,17 @@ private:
m_command_handler.get_executor(), m_command_handler.get_executor(),
[this,conn,command]() { [this,conn,command]() {
// This code runs in the command handler strand. // This code runs in the command handler strand.
// Define helper function which will be called by
// the command handler to pass a response.
auto respond = [this,conn](std::string response) {
// Post the response back to our own strand.
asio::post(
m_strand,
[this,conn,response]() {
// This code runs in our own strand.
// Handle the response.
process_response(conn, response);
});
};
// Tell the command handler to run the command. // Tell the command handler to run the command.
m_command_handler.handle_command(command, respond); auto response = m_command_handler.handle_command(command);
// Post the response back to our own strand.
asio::post(
m_strand,
[this,conn,response]() {
// This code runs in our own strand.
// Handle the response.
process_response(conn, response);
});
}); });
} }
@ -1938,7 +1878,8 @@ int run_remote_control_server(
ControlServer control_server(io, command_handler, 5025); ControlServer control_server(io, command_handler, 5025);
command_handler.set_control_server(control_server); command_handler.set_control_server(control_server);
command_handler.set_data_servers(acq_server, timetagger_server); command_handler.add_data_server(acq_server);
command_handler.add_data_server(timetagger_server);
// Disable DMA on exit from this function. // Disable DMA on exit from this function.
struct ScopeGuard { struct ScopeGuard {

View File

@ -1,2 +1,2 @@
#define PUZZLEFW_SW_MAJOR 1 #define PUZZLEFW_SW_MAJOR 1
#define PUZZLEFW_SW_MINOR 1 #define PUZZLEFW_SW_MINOR 0