Skip to content
Start free evaluation

pdcomm::TcpEndpoint

Class

Move-only owner of one HSMS (SEMI E37) endpoint over TCP/IP.

class TcpEndpoint

Declared in: #include <pdcomm_tcp.hpp>

An endpoint is one HSMS connection role. An active endpoint connects to the peer and sends Select.req after each connection; a passive endpoint listens, accepts one connection at a time and answers the peer's Select.req. The endpoint starts no threads, invokes no callbacks and resolves no host names. You drive it from your own loop:

  1. Construct it with a numeric address and a filled PdcommTcpOptions. A passive endpoint binds its listening socket during construction.
  2. Call start() to begin the first connection generation.
  3. Call poll() repeatedly to perform socket I/O and run the HSMS timers, then drain nextEvent() until it returns an empty event.
  4. In the SELECTED state, send primaries with request() and answer received primaries that have the W-bit set with reply().
  5. For a graceful close, call stop(), keep polling and draining events until state().closed is 1, and then destroy the endpoint.

Every connection attempt starts a new generation. Each submission returns a PdcommTcpToken that carries the endpoint identity, the generation and the HSMS system bytes; a token or event from another endpoint or an earlier generation is stale. A successful submission means only that the message was queued: replies, timeouts and failures arrive later as events.

Every call on an empty endpoint (default-constructed or moved from) throws pdcomm::Error with PDCOMM_INVALID_ARGUMENT. After an internal panic, every call throws with PDCOMM_POISONED and only destruction remains useful. When nextEvent() cannot allocate an event, the event is staged inside the endpoint; until nextEvent() returns it, start(), stop(), poll(), the submit calls and cancel() throw with PDCOMM_BACKPRESSURE.

GemHost and GemEquipment own a TcpEndpoint; reach it with GemEndpoint::tcp() and pass every event from nextEvent() to GemEndpoint::handleEvent() in queue order.

The sketch below answers every received W-bit primary with an SxF0 abort reply:

pdcomm::TcpEndpoint endpoint("127.0.0.1:5000", options);
endpoint.start();
while (running) {
    endpoint.poll();
    while (auto event = endpoint.nextEvent()) {
        const auto info = event.info();
        if (info.kind == PDCOMM_TCP_RECEIVED && !info.is_reply && info.reply_expected) {
            endpoint.reply(event, info.stream, 0, false, nullptr, 0);
        }
    }
}
endpoint.stop();
while (!endpoint.state().closed) {
    endpoint.poll();
    while (endpoint.nextEvent()) {}
}

Notes

  • Move-only. The endpoint is not internally synchronized: serialize every call on it, including getters, GEM calls on the same endpoint and destruction. A quiescent endpoint may move to another thread. Calls that build strings or vectors can also throw std::bad_alloc.

Members

TcpEndpoint

Constructor

Creates an empty endpoint that owns no native handle.

TcpEndpoint() noexcept = default

Declared in: #include <pdcomm_tcp.hpp>

Every call on an empty endpoint throws pdcomm::Error with PDCOMM_INVALID_ARGUMENT. Move a constructed endpoint into it before use.

TcpEndpoint

Constructor

Takes ownership of the native handle owned by other.

TcpEndpoint(TcpEndpoint &&other) noexcept

Declared in: #include <pdcomm_tcp.hpp>

The native endpoint keeps its identity and generation, so its tokens and events stay valid with this wrapper.

Parameters

NameDescription
otherSource wrapper; it becomes empty.

TcpEndpoint

Constructor

Copying is disabled; the wrapper exclusively owns its native handle.

TcpEndpoint(const TcpEndpoint &) = delete

Declared in: #include <pdcomm_tcp.hpp>

TcpEndpoint

Constructor

Creates an HSMS endpoint with pdcomm_tcp_create().

TcpEndpoint(const char *address, size_t addressLength, const PdcommTcpOptions &options)

Declared in: #include <pdcomm_tcp.hpp>

The address and options are validated and copied. A passive endpoint binds its listening socket during this call; an active endpoint performs no I/O until poll() runs a connection attempt. The new endpoint is stopped; call start() to begin.

Parameters

NameDescription
addressNumeric socket address as UTF-8 text without a NUL terminator, such as 127.0.0.1:5000 or [::1]:5000. Host names are not resolved. In active mode it is the peer address; in passive mode it is the local listening address, where port 0 selects an ephemeral port each time the socket is bound (read it with localAddress()).
addressLengthNumber of bytes at address.
optionsEndpoint configuration. Every timer and limit is application policy; PDComm supplies no defaults and does not substitute values from SEMI E37.

Notes

  • Throws pdcomm::Error: PDCOMM_INVALID_ARGUMENT when the address is not a numeric socket address or an option is invalid, for example a zero timer, a wrong version or struct_size, a nonzero reserved field or an unknown mode; PDCOMM_LIMIT_EXCEEDED when max_events is below 72, max_write_bytes is below max_frame_bytes, or max_application_bytes is below max_frame_bytes minus 14; PDCOMM_IO_ERROR when the passive listening socket cannot be bound; PDCOMM_ALLOCATION_FAILED or PDCOMM_INTERNAL_ERROR.

TcpEndpoint

Constructor

Creates an HSMS endpoint from an address string.

TcpEndpoint(const std::string &address, const PdcommTcpOptions &options)

Declared in: #include <pdcomm_tcp.hpp>

Same as the constructor that takes a pointer and a length, called with the bytes of address.

Parameters

NameDescription
addressNumeric socket address such as 127.0.0.1:5000 or [::1]:5000; host names are not resolved.
optionsEndpoint configuration; copied during the call.

Notes

  • Throws pdcomm::Error: Same statuses as the constructor that takes a pointer and a length.

cancel

Method

Cancels an open transaction with pdcomm_tcp_cancel().

bool cancel(PdcommTcpToken token)

Declared in: #include <pdcomm_tcp.hpp>

A cancelled transaction produces a PDCOMM_TCP_FAILED event with PDCOMM_TCP_CANCELLED, and a later reply to it is reported as PDCOMM_TCP_UNEXPECTED_REPLY. A queued frame that has not been written is discarded. Cancelling a frame that is partially written closes the connection, because bytes already on the wire cannot be retracted.

Parameters

NameDescription
tokenToken from request(), select(), deselect() or linktest(); reserved must be 0.

Returns

true when an open transaction was cancelled; false when it had already ended.

Notes

  • Throws pdcomm::Error: PDCOMM_INVALID_ARGUMENT when token.reserved is nonzero; PDCOMM_STALE_TOKEN when the token belongs to another endpoint or an earlier generation; PDCOMM_BACKPRESSURE when an event is staged, the event queue is full or the endpoint is stopping; PDCOMM_POISONED.

deselect

Method

Sends Deselect.req to leave the SELECTED state, with pdcomm_tcp_deselect().

PdcommTcpToken deselect()

Declared in: #include <pdcomm_tcp.hpp>

When the peer answers with DeselectStatus 0, every open transaction fails with PDCOMM_TCP_DESELECTED, unanswered received primaries are dropped, and the endpoint returns to NOT SELECTED with the T7 timer running. A nonzero status produces a PDCOMM_TCP_FAILED event with PDCOMM_TCP_DESELECT_STATUS for the returned token. Peers that implement only HSMS-SS (SEMI E37.1) might not support Deselect.

Returns

The control transaction token.

Notes

  • Throws pdcomm::Error: PDCOMM_INVALID_STATE when the endpoint is not SELECTED; PDCOMM_BACKPRESSURE when a Deselect is already outstanding, all transaction slots are in use, an event is staged, a queue is full or the endpoint is stopping; PDCOMM_LIMIT_EXCEEDED when the system bytes are exhausted; PDCOMM_POISONED.

isPending

Method

Reports whether a transaction still waits for its response, with pdcomm_tcp_is_pending().

bool isPending(PdcommTcpToken token) const

Declared in: #include <pdcomm_tcp.hpp>

Tokens of requests sent without the W-bit are never pending. This call works while an event is staged.

Parameters

NameDescription
tokenToken to check; reserved must be 0.

Returns

true while the transaction is open.

Notes

  • Throws pdcomm::Error: PDCOMM_INVALID_ARGUMENT when token.reserved is nonzero; PDCOMM_STALE_TOKEN when the token belongs to another endpoint or an earlier generation; PDCOMM_POISONED.

linktest

Method

Sends Linktest.req to check that the connection is alive, with pdcomm_tcp_linktest().

PdcommTcpToken linktest()

Declared in: #include <pdcomm_tcp.hpp>

A successful Linktest.rsp produces no event: the token simply stops being pending (see isPending()). A missing response produces a PDCOMM_TCP_FAILED event with PDCOMM_TCP_T6_TIMEOUT and closes the connection. Only one Linktest can be outstanding at a time.

Returns

The control transaction token.

Notes

  • Throws pdcomm::Error: PDCOMM_INVALID_STATE when the endpoint is not connected (NOT SELECTED or SELECTED); PDCOMM_BACKPRESSURE when a Linktest is already outstanding, all transaction slots are in use, an event is staged, a queue is full or the endpoint is stopping; PDCOMM_LIMIT_EXCEEDED when the system bytes are exhausted; PDCOMM_POISONED.

localAddress

Method

Returns the local socket address as text.

std::string localAddress() const

Declared in: #include <pdcomm_tcp.hpp>

A passive endpoint reports its listening address while it owns a listening socket: from construction until stop(), and again after start(). Use it to learn the port chosen for port 0. An active endpoint reports the local address of its current connection.

Returns

The address, such as 127.0.0.1:5000 or [::1]:5000; at most 80 bytes.

Notes

  • Throws pdcomm::Error: PDCOMM_IO_ERROR when there is no listening socket or connection, or the operating system cannot report the address; PDCOMM_POISONED.

nativeHandle

Method

Returns the owned native handle for direct C API calls.

PdcommTcpEndpoint *nativeHandle() noexcept

Declared in: #include <pdcomm_tcp.hpp>

Returns

The handle, or nullptr for an empty endpoint. Ownership stays with this wrapper.

nativeHandle

Method

Returns the owned native handle for read-only C API calls.

const PdcommTcpEndpoint *nativeHandle() const noexcept

Declared in: #include <pdcomm_tcp.hpp>

Returns

The handle, or nullptr for an empty endpoint. Ownership stays with this wrapper.

nextEvent

Method

Removes the oldest event from the endpoint's event queue, with pdcomm_tcp_next_event().

TcpEvent nextEvent()

Declared in: #include <pdcomm_tcp.hpp>

Events are returned in the order they occurred. Drain the queue after every poll(): while the queue is full, the endpoint holds further events back.

Returns

The next event, or an empty TcpEvent when the queue is empty.

Notes

  • Throws pdcomm::Error: PDCOMM_ALLOCATION_FAILED when the event could not be allocated; the event stays staged and the next call returns it, and until then start(), stop(), poll(), the submit calls and cancel() throw with PDCOMM_BACKPRESSURE; PDCOMM_POISONED.
  • With a GEM role attached, still retrieve every event here and pass it to GemEndpoint::handleEvent().

operator bool

Method

Reports whether the wrapper owns a native endpoint.

explicit operator bool() const noexcept

Declared in: #include <pdcomm_tcp.hpp>

Returns

true when an endpoint is owned.

operator=

Method

Releases the current handle, then takes ownership of the handle owned by other.

TcpEndpoint &operator=(TcpEndpoint &&other) noexcept

Declared in: #include <pdcomm_tcp.hpp>

Releasing closes the current endpoint's sockets immediately, as the destructor does.

Parameters

NameDescription
otherSource wrapper; it becomes empty.

Returns

This wrapper.

operator=

Method

Copy assignment is disabled; the wrapper exclusively owns its native handle.

TcpEndpoint &operator=(const TcpEndpoint &) = delete

Declared in: #include <pdcomm_tcp.hpp>

poll

Method

Performs pending socket I/O and HSMS timer processing with pdcomm_tcp_poll().

size_t poll()

Declared in: #include <pdcomm_tcp.hpp>

One call performs up to PdcommTcpOptions::poll_budget internal actions: connecting or accepting, reading and writing frames, answering control messages, expiring the T3, T5, T6, T7 and T8 timers, and moving results to the event queue. Retrieve the results with nextEvent(). Failures of the connection itself arrive as events, not as exceptions.

Returns

Number of internal actions that made progress; not the number of events queued.

Notes

  • Throws pdcomm::Error: PDCOMM_BACKPRESSURE when an event is staged or internal queues cannot accept more results; PDCOMM_IO_ERROR when accepting a connection failed; PDCOMM_LIMIT_EXCEEDED when a timer deadline overflows; PDCOMM_INTERNAL_ERROR when the monotonic clock went backwards; PDCOMM_POISONED.
  • An active connection attempt blocks the call for up to PdcommTcpOptions::connect_timeout_ms. An empty event queue does not mean that the session is finished; keep polling while the endpoint is in use.
  • When a GEM role is attached, the call also updates the GEM state.

reply

Method

Submits the reply to a received W-bit primary, with a body held in a vector.

void reply(const TcpEvent &primary, uint8_t stream, uint8_t function, bool wait, const std::vector<uint8_t> &body)

Declared in: #include <pdcomm_tcp.hpp>

Same as the overload that takes a pointer and a length, called with the bytes of body. An empty vector sends a reply without a body.

Parameters

NameDescription
primaryThe PDCOMM_TCP_RECEIVED event of the primary; only read.
streamSECS-II stream; must equal the primary's stream.
functionThe primary's function plus 1, or 0 for an SxF0 abort reply.
waitMust be false, because a reply cannot set the W-bit.
bodyOne complete encoded SECS-II item, or empty. Read only during the call.

Notes

  • Throws pdcomm::Error: Same statuses as the overload that takes a pointer and a length.

reply

Method

Submits the reply to a received primary that has the W-bit set, with pdcomm_tcp_reply().

void reply(const TcpEvent &primary, uint8_t stream, uint8_t function, bool wait, const uint8_t *body, size_t bodyLength)

Declared in: #include <pdcomm_tcp.hpp>

The reply reuses the primary's system bytes. Each W-bit primary occupies one of PDCOMM_TCP_MAX_TRANSACTIONS incoming slots until you reply or the connection is deselected or closed, so reply to every one. The body is validated and copied. Success means only that the reply was queued.

Parameters

NameDescription
primaryThe PDCOMM_TCP_RECEIVED event of the primary, from this endpoint and the current generation. It is only read; you keep ownership.
streamSECS-II stream; must equal the primary's stream.
functionThe primary's function plus 1, or 0 for an SxF0 abort reply.
waitMust be false, because a reply cannot set the W-bit.
bodyOne complete encoded SECS-II item, or nullptr with bodyLength 0 for a reply without a body. Read only during the call.
bodyLengthNumber of bytes at body; at most max_frame_bytes minus 14.

Notes

  • Throws pdcomm::Error: PDCOMM_INVALID_ARGUMENT when primary is empty or is not a received primary, the primary has no W-bit or was already answered, stream, function or wait does not form a valid reply, body is nullptr with a nonzero length, or the body does not fit in a frame; PDCOMM_STALE_TOKEN when primary comes from another endpoint or an earlier generation; PDCOMM_INVALID_DATA or PDCOMM_LIMIT_EXCEEDED for an invalid or oversized body; PDCOMM_INVALID_STATE when the endpoint is not SELECTED; PDCOMM_BACKPRESSURE when an event is staged, a queue is full or the endpoint is stopping; PDCOMM_POISONED.
  • With a GEM role attached, the library answers many primaries itself and others through its respond calls. Use this call for primaries that GemEndpoint::handleEvent() reports as GemResultKind::Unhandled.

request

Method

Submits a primary data message whose body is held in a vector.

PdcommTcpToken request(uint8_t stream, uint8_t function, bool wait, const std::vector<uint8_t> &body)

Declared in: #include <pdcomm_tcp.hpp>

Same as the overload that takes a pointer and a length, called with the bytes of body. An empty vector sends a message without a body.

Parameters

NameDescription
streamSECS-II stream, 0 to 127.
functionSECS-II function; must be odd (a primary). With wait it must not be 255.
waittrue to set the W-bit and expect a reply.
bodyOne complete encoded SECS-II item, or empty. Read only during the call.

Returns

The transaction token.

Notes

  • Throws pdcomm::Error: Same statuses as the overload that takes a pointer and a length.

request

Method

Submits a primary data message with pdcomm_tcp_request().

PdcommTcpToken request(uint8_t stream, uint8_t function, bool wait, const uint8_t *body, size_t bodyLength)

Declared in: #include <pdcomm_tcp.hpp>

The body is validated against PdcommTcpOptions::item_limits and copied, so you can release it when the call returns. With wait set, the W-bit is set and the endpoint opens a transaction: the matching reply arrives as a PDCOMM_TCP_RECEIVED event with is_reply set and this token, or the transaction ends with a PDCOMM_TCP_FAILED event, for example after the T3 reply timeout. Without wait, no reply is tracked and a PDCOMM_TCP_W0_WRITTEN event reports when the frame was written to the local socket.

Parameters

NameDescription
streamSECS-II stream, 0 to 127.
functionSECS-II function; must be odd (a primary). With wait it must not be 255.
waittrue to set the W-bit and expect a reply.
bodyOne complete encoded SECS-II item, or nullptr with bodyLength 0 for a message without a body. Read only during the call.
bodyLengthNumber of bytes at body; at most max_frame_bytes minus 14.

Returns

The transaction token. Keep it to match the reply or failure event, or to call cancel() or isPending().

Notes

  • Throws pdcomm::Error: PDCOMM_INVALID_STATE when the endpoint is not SELECTED; PDCOMM_INVALID_ARGUMENT when stream or function is invalid, body is nullptr with a nonzero length, or the body does not fit in a frame; PDCOMM_INVALID_DATA when the body is not exactly one valid SECS-II item; PDCOMM_LIMIT_EXCEEDED when the body exceeds item_limits or the system bytes are exhausted; PDCOMM_BACKPRESSURE when an event is staged, all transaction slots are in use, a queue is full or the endpoint is stopping; PDCOMM_POISONED.

select

Method

Sends Select.req to enter the SELECTED state, with pdcomm_tcp_select().

PdcommTcpToken select()

Declared in: #include <pdcomm_tcp.hpp>

An active endpoint already sends Select.req automatically after each connection; use this call to select again after a Deselect or a rejected selection. Success moves the endpoint to PDCOMM_TCP_SELECTED, reported as a PDCOMM_TCP_STATE_CHANGED event. A failure produces a PDCOMM_TCP_FAILED event for the returned token, for example with PDCOMM_TCP_SELECT_STATUS or PDCOMM_TCP_T6_TIMEOUT.

Returns

The control transaction token.

Notes

  • Throws pdcomm::Error: PDCOMM_INVALID_STATE when the endpoint is not in the NOT SELECTED state; PDCOMM_BACKPRESSURE when a Select is already outstanding, all transaction slots are in use, an event is staged, a queue is full or the endpoint is stopping; PDCOMM_LIMIT_EXCEEDED when the system bytes are exhausted; PDCOMM_POISONED.

start

Method

Starts a new connection generation with pdcomm_tcp_start().

void start()

Declared in: #include <pdcomm_tcp.hpp>

The endpoint increments its generation and starts connecting (active) or listening (passive). No connection is made here: poll() performs the connect or accept. After a stop, a passive endpoint binds its listening socket again.

Notes

  • Throws pdcomm::Error: PDCOMM_INVALID_STATE when the endpoint is not stopped or is still closing its previous connection; PDCOMM_BACKPRESSURE when an event is staged or the event queue is full; PDCOMM_IO_ERROR when the passive socket cannot be bound again; PDCOMM_POISONED.
  • When a GEM role is attached, the call also updates the GEM state.

state

Method

Reads the endpoint identity, generation, HSMS state and close status.

PdcommTcpState state() const

Declared in: #include <pdcomm_tcp.hpp>

state holds a PDCOMM_TCP_* state value such as PDCOMM_TCP_SELECTED. closed becomes 1 only after stop(), once the connection is closed and internal work is complete; it does not mean that every event has been retrieved. This call works while an event is staged.

Returns

The snapshot filled by pdcomm_tcp_state().

Notes

  • Throws pdcomm::Error: PDCOMM_POISONED.

stop

Method

Stops the endpoint and starts a graceful close with pdcomm_tcp_stop().

void stop()

Declared in: #include <pdcomm_tcp.hpp>

In the SELECTED state the endpoint queues Separate.req. Every open transaction fails with PDCOMM_TCP_CANCELLED, unanswered received primaries are dropped, and the state becomes PDCOMM_TCP_STOPPED. The connection stays open for up to PdcommTcpOptions::shutdown_timeout_ms while poll() writes queued frames, so keep polling and draining events until state().closed is 1. Stopping again while stopping has no effect, and stopping an endpoint that was never started succeeds.

Notes

  • Throws pdcomm::Error: PDCOMM_BACKPRESSURE when an event is staged or the event queue cannot hold the shutdown events; PDCOMM_POISONED.
  • When a GEM role is attached, the call also updates the GEM state.

~TcpEndpoint

Method

Releases the endpoint and its sockets with pdcomm_tcp_destroy().

~TcpEndpoint()

Declared in: #include <pdcomm_tcp.hpp>

The sockets close immediately; a graceful HSMS shutdown on the wire is not promised. For a graceful close, call stop() first and keep polling and draining events until state().closed is 1. Events and GEM results obtained earlier stay valid. An empty endpoint releases nothing.