18bool IsBroadcastOrMulticast(
const MacAddress& mac)
20 return mac ==
MacAddress{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} ||
21 (mac[0] & 0x01U) != 0U;
29bool ReadEthernetAddresses(
const std::vector<std::uint8_t>&
frame,
33 if (
frame.size() < 12U)
38 std::copy_n(
frame.begin(), destination.size(), destination.begin());
39 std::copy_n(
frame.begin() + 6, source.size(), source.begin());
47 : bus(busRef), localMac(localMacRef)
60 std::lock_guard<std::mutex> lock(
mutex);
63 throw std::runtime_error(
"In-memory Ethernet socket is disconnected");
72 if (value.count() < 0)
74 throw std::invalid_argument(
"In-memory Ethernet socket timeout must be >= 0 ms");
76 std::lock_guard<std::mutex> lock(
mutex);
83 std::lock_guard<std::mutex> lock(
mutex);
95 std::lock_guard<std::mutex> lock(
mutex);
101 std::unique_lock<std::mutex> lock(
mutex);
103 const auto hasData = [
this]
133 std::lock_guard<std::mutex> lock(
mutex);
145 if (socket ==
nullptr)
147 throw std::invalid_argument(
"Cannot attach a null in-memory Ethernet socket");
149 std::lock_guard<std::mutex> lock(
mutex);
158 std::lock_guard<std::mutex> busLock(
mutex);
161 if (socket !=
nullptr)
163 std::lock_guard<std::mutex> socketLock(socket->
mutex);
168 if (socket !=
nullptr)
175 const std::vector<std::uint8_t>&
frame)
179 if (!ReadEthernetAddresses(
frame, destination, source))
181 throw std::invalid_argument(
182 "In-memory Ethernet frame is shorter than an Ethernet header");
186 std::lock_guard<std::mutex> lock(
mutex);
189 if (endpoint ==
nullptr || endpoint == &sender)
196 if (endpoint->LocalMac() == source)
201 const bool destinationMatches =
202 IsBroadcastOrMulticast(destination) || endpoint->LocalMac() == destination;
203 if (destinationMatches)
205 endpoint->Enqueue(
frame);
211std::unique_ptr<InMemoryEthernetSocket>
214 return std::make_unique<InMemoryEthernetSocket>(*
this, localMac);
219 std::lock_guard<std::mutex> lock(
mutex);
225 std::lock_guard<std::mutex> busLock(
mutex);
228 if (endpoint ==
nullptr)
232 std::lock_guard<std::mutex> socketLock(endpoint->mutex);
233 endpoint->receiveQueue.clear();
Small virtual Ethernet switch suitable for deterministic tests and demos.
void Transmit(InMemoryEthernetSocket &sender, const std::vector< std::uint8_t > &frame)
Route a frame to matching endpoints.
std::size_t EndpointCount() const
Return the number of currently connected socket endpoints.
void Attach(InMemoryEthernetSocket *socket)
Attach one socket.
void Detach(InMemoryEthernetSocket *socket)
Detach one socket.
std::unique_ptr< InMemoryEthernetSocket > CreateSocket(MacAddress localMac)
Create one virtual raw-Ethernet endpoint.
void ClearQueues()
Remove all queued frames without disconnecting endpoints.
std::vector< InMemoryEthernetSocket * > endpoints
Injectable raw-Ethernet endpoint attached to an in-memory bus.
void Enqueue(const std::vector< std::uint8_t > &frame)
Queue one frame for this endpoint.
void SetTimeout(std::chrono::milliseconds timeout) override
Set the maximum time spent waiting for a frame.
void Send(const std::vector< std::uint8_t > &frame) override
Transmit one complete Ethernet frame onto the virtual bus.
~InMemoryEthernetSocket() override
Disconnect and detach the endpoint.
bool ReceiveEnabled() const noexcept
Return whether this endpoint currently accepts incoming frames.
std::chrono::milliseconds timeout
std::vector< std::uint8_t > Recv() override
Receive one frame or an empty vector after a timeout.
void SetReceiveEnabled(bool enabled)
Enable or disable frame delivery into this endpoint receive queue.
InMemoryEthernetBus & bus
std::deque< std::vector< std::uint8_t > > receiveQueue
InMemoryEthernetSocket(InMemoryEthernetBus &bus, MacAddress localMac)
Construct and attach a virtual endpoint.
std::condition_variable condition
Thread-safe in-memory Layer-2 Ethernet switch for PROFINET simulation.
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.