Start working on remote control server
This commit is contained in:
parent
66050aca5b
commit
ea5d3c3a1d
|
@ -1,5 +1,6 @@
|
|||
devicetree/devicetree.dtb
|
||||
buildroot_overlay/opt/puzzlefw/bin/puzzlecmd
|
||||
buildroot_overlay/opt/puzzlefw/bin/remotectl
|
||||
buildroot_overlay/opt/puzzlefw/driver/
|
||||
downloads/
|
||||
buildroot-2023.02.8/
|
||||
|
|
|
@ -8,17 +8,19 @@ CFLAGS = -Wall -O2
|
|||
CXXFLAGS = -std=c++17 -Wall -Wno-psabi -O2
|
||||
|
||||
.PHONY: all
|
||||
all: puzzlecmd
|
||||
all: puzzlecmd remotectl
|
||||
|
||||
puzzlecmd: puzzlecmd.o puzzlefw.o
|
||||
$(CXX) -o $@ $(LDFLAGS) $^
|
||||
|
||||
puzzlecmd.o: puzzlecmd.cpp logging.hpp puzzlefw.hpp interrupt_manager.hpp data_server.hpp
|
||||
puzzlefw.o: puzzlefw.cpp puzzlefw.hpp
|
||||
remotectl: remotectl.o puzzlefw.o
|
||||
$(CXX) -o $@ $(LDFLAGS) $^
|
||||
|
||||
testje: testje.c
|
||||
puzzlecmd.o: puzzlecmd.cpp logging.hpp puzzlefw.hpp interrupt_manager.hpp data_server.hpp
|
||||
remotectl.o: remotectl.cpp logging.hpp puzzlefw.hpp interrupt_manager.hpp data_server.hpp version.hpp
|
||||
puzzlefw.o: puzzlefw.cpp puzzlefw.hpp
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(RM) -f -- puzzlecmd testje *.o
|
||||
$(RM) -f -- puzzlecmd remotectl *.o
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <stdint.h>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
|
@ -82,11 +83,17 @@ public:
|
|||
m_dma_stream.disable_interrupt();
|
||||
}
|
||||
|
||||
/** 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.
|
||||
*
|
||||
* This method must not be called directly while a multi-threaded Asio
|
||||
* event loop is running. In that case, use "async_start_server()".
|
||||
* If a multi-threaded Asio event loop is running, this method may only
|
||||
* be called through the strand returned by "get_executor()".
|
||||
*/
|
||||
void start_server()
|
||||
{
|
||||
|
@ -113,7 +120,7 @@ public:
|
|||
|
||||
// Asynchronously accept connections.
|
||||
m_acceptor.async_accept(
|
||||
[this](auto& ec, auto s){ handle_accept(ec, std::move(s)); });
|
||||
[this](auto ec, auto s){ handle_accept(ec, std::move(s)); });
|
||||
|
||||
log(LOG_INFO, "Ready for TCP connections to port %d", m_tcp_port);
|
||||
}
|
||||
|
@ -121,8 +128,8 @@ public:
|
|||
/**
|
||||
* Stop the server and close current connections.
|
||||
*
|
||||
* This method must not be called directly while a multi-threaded Asio
|
||||
* event loop is running. In that case, use "async_stop_server()".
|
||||
* If a multi-threaded Asio event loop is running, this method may only
|
||||
* be called through the strand returned by "get_executor()".
|
||||
*/
|
||||
void stop_server()
|
||||
{
|
||||
|
@ -144,46 +151,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a call to "start_server()" to be executed in the strand
|
||||
* of this server instance.
|
||||
*
|
||||
* After starting the server, the specified completion handler will
|
||||
* be submitted for execution.
|
||||
*
|
||||
* This method may safely be called from any thread.
|
||||
*/
|
||||
template <typename Handler>
|
||||
void async_start_server(Handler&& handler)
|
||||
{
|
||||
asio::post(
|
||||
m_strand,
|
||||
[this, handler] () mutable {
|
||||
start_server();
|
||||
asio::post(m_strand, handler);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a call to "stop_server()" to be executed in the strand
|
||||
* of this server instance.
|
||||
*
|
||||
* After stopping the server, the specified completion handler will
|
||||
* be submitted for execution.
|
||||
*
|
||||
* This method may safely be called from any thread.
|
||||
*/
|
||||
template <typename Handler>
|
||||
void async_stop_server(Handler&& handler)
|
||||
{
|
||||
asio::post(
|
||||
m_strand,
|
||||
[this, handler] () mutable {
|
||||
stop_server();
|
||||
asio::post(m_strand, handler);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an FPGA interrupt occurs.
|
||||
*
|
||||
|
@ -206,7 +173,7 @@ private:
|
|||
/**
|
||||
* Re-initialize DMA stream and discard stale data from the DMA buffer.
|
||||
*
|
||||
* Do not call this while an async_send() operation is in progress.
|
||||
* Do not call this while an async_write() operation is in progress.
|
||||
*/
|
||||
void discard_stale_data()
|
||||
{
|
||||
|
@ -228,7 +195,7 @@ private:
|
|||
}
|
||||
|
||||
/** Accept completion handler. */
|
||||
void handle_accept(const boost::system::error_code& error,
|
||||
void handle_accept(boost::system::error_code error,
|
||||
asio::ip::tcp::socket conn)
|
||||
{
|
||||
if (error) {
|
||||
|
@ -256,7 +223,7 @@ private:
|
|||
// Retry accept call.
|
||||
if (m_acceptor.is_open()) {
|
||||
m_acceptor.async_accept(
|
||||
[this](auto& ec, auto s) {
|
||||
[this](auto ec, auto s) {
|
||||
handle_accept(ec, std::move(s));
|
||||
});
|
||||
}
|
||||
|
@ -281,7 +248,7 @@ private:
|
|||
if (m_acceptor.is_open()) {
|
||||
// Continue accepting connections.
|
||||
m_acceptor.async_accept(
|
||||
[this](auto& ec, auto s){ handle_accept(ec, std::move(s)); });
|
||||
[this](auto ec, auto s){ handle_accept(ec, std::move(s)); });
|
||||
}
|
||||
|
||||
if (m_connection.is_open()) {
|
||||
|
@ -302,7 +269,7 @@ private:
|
|||
// closed remotely (or client writes unexpected data).
|
||||
m_connection.async_receive(
|
||||
asio::buffer(m_receive_buf, sizeof(m_receive_buf)),
|
||||
[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.
|
||||
|
@ -314,7 +281,7 @@ private:
|
|||
}
|
||||
|
||||
/** Receive completion handler. */
|
||||
void handle_receive(const boost::system::error_code& error, size_t len)
|
||||
void handle_receive(boost::system::error_code error, size_t len)
|
||||
{
|
||||
if (m_stale_receive) {
|
||||
// This completion refers to an old, already closed connection.
|
||||
|
@ -324,7 +291,7 @@ private:
|
|||
// Initiate async receive for the new connection.
|
||||
m_connection.async_receive(
|
||||
asio::buffer(m_receive_buf, sizeof(m_receive_buf)),
|
||||
[this](auto& ec, size_t n){ handle_receive(ec, n); });
|
||||
[this](auto ec, size_t n){ handle_receive(ec, n); });
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -359,10 +326,9 @@ private:
|
|||
}
|
||||
|
||||
/** Send completion handler. */
|
||||
void handle_send(const boost::system::error_code& error, size_t len)
|
||||
void handle_send(boost::system::error_code error, size_t len)
|
||||
{
|
||||
assert(m_send_in_progress);
|
||||
assert(m_send_size > 0);
|
||||
|
||||
m_send_in_progress = false;
|
||||
|
||||
|
@ -394,21 +360,13 @@ private:
|
|||
return;
|
||||
}
|
||||
|
||||
if (len < m_send_buffer.size()) {
|
||||
// Partially completed. Send the rest.
|
||||
m_send_buffer += len;
|
||||
m_send_in_progress = true;
|
||||
m_connection.async_send(
|
||||
m_send_buffer,
|
||||
[this](auto& ec, size_t n){ handle_send(ec, n); });
|
||||
} else {
|
||||
// Fully completed.
|
||||
// Release the completed part of the DMA buffer.
|
||||
m_dma_stream.consume_data(m_send_size);
|
||||
// Send operation completed.
|
||||
// Release the completed part of the DMA buffer.
|
||||
assert(len == m_send_buffer.size());
|
||||
m_dma_stream.consume_data(m_send_buffer.size());
|
||||
|
||||
// Try to send more data.
|
||||
transmit_data(false);
|
||||
}
|
||||
// Try to send more data.
|
||||
transmit_data(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -447,7 +405,7 @@ private:
|
|||
// If timeout occurs, we will send whatever data is available.
|
||||
m_timer.expires_after(WAIT_TIMEOUT);
|
||||
m_timer.async_wait(
|
||||
[this](auto& ec){ handle_timer(ec); });
|
||||
[this](auto ec){ handle_timer(ec); });
|
||||
|
||||
// Done. We will be notified for interrupt or timeout.
|
||||
return;
|
||||
|
@ -467,16 +425,15 @@ private:
|
|||
// Initiate async send.
|
||||
// Limit the block size so we can release that part of the buffer
|
||||
// as soon as it completes.
|
||||
m_send_size = std::min(data_available, SEND_MAX_BLOCK);
|
||||
m_send_buffer = asio::buffer(data, m_send_size);
|
||||
size_t block_size = std::min(data_available, SEND_MAX_BLOCK);
|
||||
m_send_buffer = asio::buffer(data, block_size);
|
||||
m_send_in_progress = true;
|
||||
m_connection.async_send(
|
||||
m_send_buffer,
|
||||
[this](auto& ec, size_t n){ handle_send(ec, n); });
|
||||
asio::async_write(m_connection, m_send_buffer,
|
||||
[this](auto ec, size_t n){ handle_send(ec, n); });
|
||||
}
|
||||
|
||||
/** Timeout handler. */
|
||||
void handle_timer(const boost::system::error_code& error)
|
||||
void handle_timer(boost::system::error_code error)
|
||||
{
|
||||
if (error) {
|
||||
// Ignore error due to cancellation.
|
||||
|
@ -520,7 +477,6 @@ private:
|
|||
bool m_stale_receive;
|
||||
bool m_stale_send;
|
||||
bool m_send_in_progress;
|
||||
size_t m_send_size;
|
||||
asio::const_buffer m_send_buffer;
|
||||
};
|
||||
|
||||
|
@ -541,7 +497,7 @@ public:
|
|||
, m_interval(interval)
|
||||
{
|
||||
m_timer.expires_after(m_interval);
|
||||
m_timer.async_wait([this](auto& ec){ handle_timer(ec); });
|
||||
m_timer.async_wait([this](auto ec){ handle_timer(ec); });
|
||||
}
|
||||
|
||||
// Delete copy constructor and assignment operator.
|
||||
|
@ -567,7 +523,7 @@ public:
|
|||
|
||||
private:
|
||||
/** Timeout handler. */
|
||||
void handle_timer(const boost::system::error_code& error)
|
||||
void handle_timer(boost::system::error_code error)
|
||||
{
|
||||
if (error) {
|
||||
// Ignore error due to cancellation.
|
||||
|
@ -580,7 +536,7 @@ private:
|
|||
}
|
||||
|
||||
m_timer.expires_after(m_interval);
|
||||
m_timer.async_wait([this](auto& ec){ handle_timer(ec); });
|
||||
m_timer.async_wait([this](auto ec){ handle_timer(ec); });
|
||||
|
||||
check_dma_error();
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ public:
|
|||
|
||||
private:
|
||||
/** Called when an FPGA interrupt occurs. */
|
||||
void handle_wait(const boost::system::error_code& error)
|
||||
void handle_wait(boost::system::error_code error)
|
||||
{
|
||||
if (error) {
|
||||
// Ignore error due to cancellation.
|
||||
|
@ -100,7 +100,7 @@ private:
|
|||
|
||||
// Wait for next interrupt.
|
||||
m_uio_fd.async_wait(asio::posix::descriptor_base::wait_read,
|
||||
[this](auto& ec){ handle_wait(ec); });
|
||||
[this](auto ec){ handle_wait(ec); });
|
||||
}
|
||||
|
||||
PuzzleFwDevice& m_device;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* puzzlecmd.cpp
|
||||
*
|
||||
* Command-line program to test PuzzleFW firmware.
|
||||
*
|
||||
* Joris van Rantwijk 2024
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
|
@ -120,7 +122,7 @@ void run_data_server(puzzlefw::PuzzleFwDevice& device)
|
|||
// Catch Ctrl-C for controlled shut down.
|
||||
asio::signal_set signals(io, SIGINT);
|
||||
signals.async_wait(
|
||||
[&io](auto& ec, int sig) {
|
||||
[&io](auto ec, int sig) {
|
||||
log(LOG_INFO, "Got SIGINT, stopping server");
|
||||
io.stop();
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,2 @@
|
|||
#define PUZZLEFW_SW_MAJOR 0
|
||||
#define PUZZLEFW_SW_MINOR 1
|
Loading…
Reference in New Issue