Skip to content

Commit

Permalink
mvlc-cli: implement new send_eth_delay command
Browse files Browse the repository at this point in the history
Simple command to send EthDelay (0x0207) commands to the MVLC ETH delay
port.
  • Loading branch information
flueke committed Feb 28, 2025
1 parent ff5d40f commit 94a397d
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 31 deletions.
68 changes: 68 additions & 0 deletions extras/mvlc-cli/mvlc-cli.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <mesytec-mvlc/mesytec-mvlc.h>
#include <mesytec-mvlc/mvlc_impl_eth.h>
#include <mesytec-mvlc/mvlc_impl_usb.h>
#include <mesytec-mvlc/util/udp_sockets.h>
#include <yaml-cpp/yaml.h>
#include <yaml-cpp/emittermanip.h>

Expand Down Expand Up @@ -1138,6 +1139,72 @@ usage: mvlc-cli show_usb_devices [--all-devices]
.exec = show_usb_devices,
};

// Note: This does not connect an MVLC instance as that class does way too much
// work for this use case and the InUse detection would get in our way. Instead
// the MVLC instance is created to get the remote host address and then a socket
// for sending data to the MVLC eth delay port is created and used.
DEF_EXEC_FUNC(send_eth_delay_command)
{
spdlog::trace("entered send_eth_delay_command()");

auto &parser = ctx.parser;
trace_log_parser_info(parser, "send_eth_delay_command");

auto delay = parse_unsigned<u16>(parser[2]);

if(!delay)
{
std::cerr << "Error: invalid delay value given\n";
return 1;
}

auto mvlc = make_mvlc_from_standard_params(parser);

if (!mvlc)
return 1;

auto ethImpl = dynamic_cast<eth::Impl *>(mvlc.getImpl());

if (!ethImpl)
{
std::cerr << "Error: send_eth_delay command requires an ETH connection\n";
return 1;
}

std::error_code ec;
auto remote = ethImpl->getRemoteAddress();
int sock = eth::connect_udp_socket(remote, eth::DelayPort, &ec);

if (ec || sock < 0)
{
std::cerr << fmt::format("Error: could not connect to remote address '{}': {}\n",
remote, ec.message());
return 1;
}

if (auto ec = eth::send_delay_command(sock, delay.value()))
{
std::cerr << fmt::format("Error: could not send EthDelay command: {}\n", ec.message());
return 1;
}

std::cout << fmt::format("Sent EthDelay command to {} with delay {} µs\n", remote, delay.value());
return 0;
}

static const Command SendEthDelayCommand
{
.name = "send_eth_delay",
.description = "Send an EthDelay command to the MVLC",
.help = R"~(
usage: mvlc-cli [--mvlc <url/ip>] send_eth_delay <delay_µs>
Send an EthDelay command to the MVLC with the given delay in microseconds.
Delay is a 16-bit value, max is 65535 µs.
)~",
.exec = send_eth_delay_command,
};

int main(int argc, char *argv[])
{
std::string generalHelp = R"~(
Expand Down Expand Up @@ -1231,6 +1298,7 @@ MVLC connection URIs:
ctx.commands.insert(VmeWriteCommand);
ctx.commands.insert(DumpRegistersCommand);
ctx.commands.insert(ShowUsbDevicesCommand);
ctx.commands.insert(SendEthDelayCommand);
ctx.parser = parser;

// mvlc-cli // show generalHelp
Expand Down
60 changes: 30 additions & 30 deletions src/mesytec-mvlc/mvlc_impl_eth.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,6 @@

using namespace mesytec::mvlc;

namespace
{

static const unsigned DefaultWriteTimeout_ms = 500;
static const unsigned DefaultReadTimeout_ms = 500;

// Amount of receive buffer space requested from the OS for both the command
// and data sockets. It's not considered an error if less buffer space is
// granted.
static const int DesiredSocketReceiveBufferSize = 1024 * 1024 * 10;

/* Ethernet throttling implementation:
* The MVLC now has a new 'delay pipe' on port 0x8002. It accepts delay
* commands only and doesn't send any responses. Delay commands carry a 16-bit
Expand All @@ -75,22 +64,22 @@ static const int DesiredSocketReceiveBufferSize = 1024 * 1024 * 10;
* fill level.
*/

std::error_code send_delay_command(int delaySock, u16 delay_us)
namespace mesytec::mvlc::eth
{
u32 cmd = static_cast<u32>(super_commands::SuperCommandType::EthDelay) << super_commands::SuperCmdShift;
cmd |= delay_us;

size_t bytesTransferred = 0;
auto ec = eth::write_to_socket(delaySock, reinterpret_cast<const u8 *>(&cmd), sizeof(cmd), bytesTransferred);
std::error_code send_delay_command(int delaySock, u16 delay_us);
}

if (ec)
return ec;
namespace
{
using namespace mesytec::mvlc::eth;

if (bytesTransferred != sizeof(cmd))
return make_error_code(MVLCErrorCode::ShortWrite);
static const unsigned DefaultWriteTimeout_ms = 500;
static const unsigned DefaultReadTimeout_ms = 500;

return {};
};
// Amount of receive buffer space requested from the OS for both the command
// and data sockets. It's not considered an error if less buffer space is
// granted.
static const int DesiredSocketReceiveBufferSize = 1024 * 1024 * 10;

// This code increases the delay value by powers of two. The max value is 64k
// so we need 16 steps to reach the maximum.
Expand Down Expand Up @@ -497,12 +486,25 @@ void mvlc_eth_throttler(

} // end anon namespace

namespace mesytec
namespace mesytec::mvlc::eth
{
namespace mvlc
{
namespace eth

std::error_code send_delay_command(int delaySock, u16 delay_us)
{
u32 cmd = static_cast<u32>(super_commands::SuperCommandType::EthDelay) << super_commands::SuperCmdShift;
cmd |= delay_us;

size_t bytesTransferred = 0;
auto ec = eth::write_to_socket(delaySock, reinterpret_cast<const u8 *>(&cmd), sizeof(cmd), bytesTransferred);

if (ec)
return ec;

if (bytesTransferred != sizeof(cmd))
return make_error_code(MVLCErrorCode::ShortWrite);

return {};
};

Impl::Impl(const std::string &host)
: m_host(host)
Expand Down Expand Up @@ -1242,6 +1244,4 @@ s32 calc_packet_loss(u16 lastPacketNumber, u16 packetNumber)
return diff - 1;
}

} // end namespace eth
} // end namespace mvlc
} // end namespace mesytec
}
5 changes: 5 additions & 0 deletions src/mesytec-mvlc/mvlc_impl_eth.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class MESYTEC_MVLC_EXPORT Impl: public MVLCBasicInterface, public MVLC_ETH_Inter

// Returns the host/IP string given to the constructor.
std::string getHost() const { return m_host; }
std::string getRemoteAddress() const { return getHost(); }

sockaddr_in getCmdSockAddress() const { return m_cmdAddr; }
sockaddr_in getDataSockAddress() const { return m_dataAddr; }
Expand Down Expand Up @@ -167,6 +168,10 @@ class MESYTEC_MVLC_EXPORT Impl: public MVLCBasicInterface, public MVLC_ETH_Inter
// packets in-between, taking overflow into account.
s32 MESYTEC_MVLC_EXPORT calc_packet_loss(u16 lastPacketNumber, u16 packetNumber);

// Sends an EthDelay (0x0207) command packet through the given socket.
// No response data; the MVLC delay port is write only.
std::error_code send_delay_command(int delaySock, u16 delay_us);

}

#endif /* __MESYTEC_MVLC_MVLC_IMPL_UDP_H__ */
2 changes: 1 addition & 1 deletion src/mesytec-mvlc/mvlc_readout_worker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ std::pair<std::error_code, size_t> readout_usb(

const size_t bytesToRead = usb::USBStreamPipeReadSize;
size_t totalBytesTransferred = 0;
size_t readCycles = 0;
[[maybe_unused]] size_t readCycles = 0;
std::error_code ec;

while (dest.size() >= bytesToRead && sw.get_elapsed() < timeout)
Expand Down

0 comments on commit 94a397d

Please sign in to comment.