Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/common/server/NetRemoteServerConfiguration.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ ConfigureCliAppOptions(CLI::App& app, NetRemoteServerConfiguration& config)
->default_val(NetRemoteServerConfiguration::LogVerbosityDefault);

app.add_option(
"-at, --att-type",
"-t,--att-type",
config.RfAttenuatorConfiguration.Type,
"The type of RF attenuator to use. Supported types: 'none', 'software', 'socket' (default: 'none')");

app.add_option(
"-aa, --att-address",
"-i,--att-address",
config.RfAttenuatorConfiguration.Address,
"The address of the RF attenuator. This is only used if the type is 'socket'");

app.add_option(
"-ap, --att-port",
"-p,--att-port",
config.RfAttenuatorConfiguration.Port,
"The port of the RF attenuator. This is only used if the type is 'socket'");
return app;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ struct RfAttenuatorAeroflexWeinschle83XXFactory : public IRfAttenuatorWithTcpCon

// These values have been determined from experimentation.
static constexpr uint32_t ReceiveSize{ 1024 };
static constexpr auto ReceiveDelay{ 1s };
static constexpr auto SettlingTime{ 1s };
// We don't need extra delay for receiving data, the delay is handled by select timeout argument.
static constexpr auto ReceiveDelay{ 0s };
static constexpr auto SettlingTime{ 0s };

static constexpr TcpTransportConfiguration TransportConfiguration{
.ReceiveSize = ReceiveSize,
Expand Down
41 changes: 35 additions & 6 deletions src/linux/rfattenuator/lib/SocketHelpers.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,26 @@
namespace detail
{
// Timeout to wait for a socket to become ready for Transmit (send()) operation.
//
// On Linux, the network takes longer time to receive the response from the AFW83 attenuator.
// Tested, it take 3 seconds to receive the response after sending the GetProperties request.
// If SettingTime is set to 1, then after GetProperties request, the response is partially received (Only the first line, *IDN?).
// After this, if we send another request, then the rest of GetProperties will be received along with the response of the second request.
// Like below
// Request: *IDN?
// AdaptResponse: *IDN?
// Request: CHAN 1; ATTN? <-- The second request is sent before all reponse of the first request is received.
// AdaptResponse: CHAN 1; ATTN?
// Weinschel, 8311-352-9-TN, 386, V2.98 <-- this is the rest of GetProperties response
// 103.00
//
// GetAttenuation takes 2 seconds to receive the response.
//
// 3 seconds is a good value to ensure that the response for different type of request is fully received.
timeval TransmitReadyTimeout{
.tv_sec = 1, // 1 second
.tv_sec = 3, // 3 seconds
.tv_usec = 0
};

// Timeout to wait for a socket to become ready for Receive (recv()) operation.
timeval ReceiveReadyTimeout = TransmitReadyTimeout;
} // namespace detail

namespace SocketHelpers
Expand Down Expand Up @@ -91,7 +104,8 @@ Transmit(const int& socket, std::span<uint8_t> buffer)
FD_SET(socket, &writeSet);

// Wait for socket write buffer to become ready for data.
const auto numSocketsReady = select(0, nullptr, &writeSet, nullptr, &detail::TransmitReadyTimeout);
auto readyTimeout = detail::TransmitReadyTimeout;
const auto numSocketsReady = select(socket + 1, nullptr, &writeSet, nullptr, &readyTimeout);
if (numSocketsReady < 0) {
throw SocketHelperException(std::format("Error while waiting for socket write buffer to become ready for data with errno=0x{:08x}", errno).c_str(), errno);
} else if (numSocketsReady == 0) {
Expand Down Expand Up @@ -128,7 +142,22 @@ Receive(const int& socket, std::span<uint8_t> buffer, std::chrono::milliseconds
FD_SET(socket, &readSet);

// Wait for data to become available for reading on the socket.
const auto numSocketsReady = select(0, &readSet, nullptr, nullptr, &detail::ReceiveReadyTimeout);
//
// On Linux, select() modifies timeout to reflect the amount of time not slept; most other implementations do not do this. (POSIX.1
// permits either behavior.) This causes problems both when Linux
// code which reads timeout is ported to other operating systems, and
// when code is ported to Linux that reuses a struct timeval for
// multiple select()s in a loop without reinitializing it. Consider
// timeout to be undefined after select() returns.
//
// So, we need to reinitialize the timeout value for each select() call. Tested on Ubuntu 24.10, readyTimeout will be changed to 0.
// After the select() call, the next select will return immediately if reuse the variable without reinitializing it.
//
// SettlingTime and ReceiveDelay may be originally introduced to mitigate this issue without reinitializing the timeout value.
// With this change, we can techinically remove the SettlingTime and ReceiveDelay. I will keep them in case we need tune them in the future.
// But I set both of them to 0s for now to avoid unnecessary delay.
auto readyTimeout = detail::TransmitReadyTimeout;
const auto numSocketsReady = select(socket + 1, &readSet, nullptr, nullptr, &readyTimeout);
if (numSocketsReady < 0) {
throw SocketHelperException(std::format("Error while waiting for data to become available with errno=0x{:08x}", errno).c_str(), errno);
} else if (numSocketsReady == 0) {
Expand Down