Status codes and errors
The PDComm SECS/GEM SDK reports failures in two ways:
- Call results report problems that are known when you make the call. C functions return a status code, the C++ wrappers throw
pdcomm::Error, and the .NET binding throwsPdcommException. - Asynchronous results report problems that appear later, after a request was sent. They arrive as HSMS events and GEM results. For example, a T3 reply timeout arrives as an event after the request call itself succeeded.
A successful request call only means that the SDK queued the request. It doesn’t mean the peer replied. Always confirm the reply or the failure through events.
C status codes
Section titled “C status codes”C functions return PdcommStatus (int32_t). The .NET binding uses the same values in PdcommStatus.
| Value | C name | .NET name | Meaning | What to do |
|---|---|---|---|---|
| 0 | PDCOMM_OK |
Ok |
The call succeeded. | Nothing. |
| 1 | PDCOMM_INVALID_ARGUMENT |
InvalidArgument |
An argument is invalid: a NULL pointer, a wrong struct_size or version, a non-numeric address, a zero timer, or an odd function number in a reply. |
Zero-initialize option structures, then set struct_size and version. Write addresses as numbers, for example 127.0.0.1:5000. |
| 2 | PDCOMM_LIMIT_EXCEEDED |
LimitExceeded |
A limit set by your application was exceeded: message size, item count, nesting depth, or number of IDs. | Limits are your policy, not standard values. Raise the limit in PdcommSecsLimits or the endpoint options if you need larger data. |
| 3 | PDCOMM_BUFFER_TOO_SMALL |
BufferTooSmall |
The output buffer is too small. required holds the size you need, and the buffer is unchanged. |
Allocate required bytes and call again. |
| 4 | PDCOMM_ALLOCATION_FAILED |
AllocationFailed |
A memory allocation failed. | If it happened while you took an event, the SDK keeps that event. Free memory and repeat the call. |
| 5 | PDCOMM_INTERNAL_ERROR |
InternalError |
The SDK caught an internal failure, or the clock went backwards. | Send the reproduction steps and the diagnostics log to support. |
| 6 | PDCOMM_INVALID_DATA |
InvalidData |
The SECS-II encoding or the data format is invalid. | In .NET, ErrorOffset gives the byte position of the problem. |
| 7 | PDCOMM_TRUNCATED |
Truncated |
The SECS-II input ends too early. | Make sure you pass all received bytes. |
| 8 | PDCOMM_OUT_OF_RANGE |
OutOfRange |
The ID doesn’t exist or the index is out of range. | Check the registered IDs and the index range. |
| 9 | PDCOMM_BACKPRESSURE |
Backpressure |
The endpoint can’t take more work right now: 32 transactions are open, the same control request is already pending, or an event queue is full. | Keep draining events, and send again after replies or failures arrive. Your application decides how often and how long to retry. |
| 10 | PDCOMM_INVALID_STATE |
InvalidState |
The call isn’t allowed in the current state, for example a request while not Selected, a status request before communication is established, or a setup call after start. | Check the state and fix the order of calls. |
| 11 | PDCOMM_STALE_TOKEN |
StaleToken |
The token or event belongs to another endpoint or an earlier generation, or GEM already processed the event. | After a reconnect, start again with new requests. Pass each event to GEM only once. |
| 12 | PDCOMM_IO_ERROR |
IoError |
A socket bind, accept, local address query, or write timed out or failed. pdcomm_license_host_id also returns it when the machine id cannot be read. |
When it happens while you create a passive endpoint, the port is usually in use. See Port already in use. When the host id query returns it, see Host id. |
| 13 | PDCOMM_POISONED |
Poisoned |
The endpoint caught an unexpected failure during a call. Every later call on that endpoint returns this value. | Destroy the endpoint and create a new one. Send the reproduction steps and the log to support. |
| 14 | PDCOMM_PROGRAM_UNCONFIRMED |
ProgramUnconfirmed |
A process program was installed, but durable storage isn’t confirmed. | Check the storage result and confirm again according to your policy. |
| 15 | PDCOMM_SPOOL_UNCONFIRMED |
SpoolUnconfirmed |
A spool change isn’t confirmed as durably stored. | Keep the queue and the storage result, and retry according to your policy. |
| 16 | PDCOMM_LICENSE_REQUIRED |
LicenseRequired |
There’s no license for new communication. Creating and starting endpoints (pdcomm_tcp_create, pdcomm_tcp_start, pdcomm_secs1_create, pdcomm_secs1_start) needs the secs-gem module, and creating or replacing a GEM300 capability catalog needs gem300. The license is missing or expired, or doesn’t list the host id of this computer. Nothing was created or started. |
Check the module’s state with pdcomm_license_status and follow Install and check a license. Endpoints that are already running keep working. |
The SECS/GEM SDK has no function that returns a last-error message. Use the status code, the codes in events, and the diagnostics log to find the cause. For PDCOMM_LICENSE_REQUIRED, the cause is in modules of the status JSON that pdcomm_license_status returns. With the diagnostics log on, every refusal also writes a license_required record.
C++ exceptions
Section titled “C++ exceptions”The C++ wrappers throw Error, which derives from std::runtime_error. Its what() text is PDComm native operation failed for every status except PDCOMM_LICENSE_REQUIRED, whose text starts with PDComm license required (status 16), so it doesn’t tell you the cause. Catch const pdcomm::Error & and log the value of Error::status. The C++ tutorial peer does this when an F0 reply fails: it prints the status after REPLY ERROR status=.
The tutorial peer prints only what() for command errors. If you type values before communication is established, all you see is this:
COMMAND ERROR PDComm native operation failedIn the same situation, the C# peer also prints the status name (InvalidState). The logging wrapper DiagnosticLogging throws std::runtime_error with Cannot configure PDComm logging, Cannot query PDComm logging, or Cannot flush PDComm logging. The license wrapper pdcomm::License (pdcomm_license.hpp) also throws std::runtime_error, with Cannot install the PDComm license, Cannot query the PDComm license status, or Cannot read the PDComm host id.
.NET exceptions
Section titled “.NET exceptions”| Exception | When it happens | What to do |
|---|---|---|
PdcommException |
A native call failed. Status holds the status code, and ErrorOffset holds the byte position of a decoding failure. The message looks like PDComm native operation failed with status InvalidState. When Status is LicenseRequired, the message ends with the module states, as in License state: secs-gem missing, gem300 missing. |
Follow the status code table above. For licenses, see the details with PDComm.License.Status(). |
System.DllNotFoundException |
PdcommRuntime.Initialize can’t load the native library from the path you passed. |
Check the absolute path, the file name, and the process architecture (x64 or arm64). |
ArgumentException |
You passed a relative path to PdcommRuntime.Initialize. |
Pass an absolute path. |
NotSupportedException |
The native library’s ABI isn’t between 0.17 and 0.30, or a required function is missing. | Use PDComm.dll and the native library from the same SDK bundle. |
InvalidOperationException |
The runtime was already initialized with a different library, or a DiagnosticLogging call failed. |
Use one native library per process. Check the logging JSON and the file path. |
TimeoutException |
A worker couldn’t drain and close before its shutdown deadline. | Make sure you keep reading events, and give the shutdown option enough time. |
When a worker fails to process an event, it doesn’t throw. It records the status in GemEvent.ProcessingStatus. When the worker’s command queue is full, you get PdcommException with Backpressure.
GEM results
Section titled “GEM results”When you pass a TCP event to GEM (pdcomm_gem_handle_event), you get a result with one of these kinds. The .NET type is GemResultKind.
| Value | C name | .NET name | Meaning |
|---|---|---|---|
| 0 | PDCOMM_GEM_IGNORED |
Ignored |
The event has nothing to do with GEM, such as a state change, protocol error, or transport error. |
| 1 | PDCOMM_GEM_PRIMARY |
Primary |
The peer sent a request (a primary message). GEM may have replied automatically. |
| 2 | PDCOMM_GEM_RESPONSE |
Response |
A valid reply to one of your requests arrived. |
| 3 | PDCOMM_GEM_FAILED |
Failed |
Your request ended without a reply. code holds the reason. |
| 4 | PDCOMM_GEM_INVALID_RESPONSE |
InvalidResponse |
A reply arrived but failed validation. code holds a GEM error. |
| 5 | PDCOMM_GEM_UNHANDLED |
Unhandled |
GEM didn’t handle a received request and sent no reply. code holds a GEM error. |
For Failed, code is an HSMS failure reason (1–8 in the table below), or 12 (PDCOMM_GEM_ERROR_ABORTED) when the peer aborted the transaction with F0. For InvalidResponse and Unhandled, code is a GEM error (GemError):
| Value | GEM error | Meaning |
|---|---|---|
| 1 | Disconnected |
No session is selected. |
| 2 | State |
The current communication or control state doesn’t allow the message. |
| 3 | Unsupported |
The stream and function aren’t supported for this role or configuration. Received S9Fx messages also get this code. |
| 4 | Wait |
The W-bit isn’t what the message requires. |
| 5 | MissingBody |
The message needs a body but has none. |
| 6 | UnexpectedBody |
The message must not have a body but has one. |
| 7 | Body |
The body structure is invalid. |
| 8 | Ack |
The acknowledge code value is invalid. |
| 9 | Response |
The reply isn’t the one the request expects. |
| 10 | Codec |
SECS-II decoding failed. |
| 11 | Correlation |
The reply doesn’t match the request. |
| 12 | Aborted |
The peer aborted the transaction with F0. |
Read the tutorial output
Section titled “Read the tutorial output”The tutorial peers print each GEM result as RESULT kind=<kind> request=<request> outcome=<outcome>. Results for received requests (kind=1) show request=0. These lines come from real runs:
| Output | Printed by | Meaning |
|---|---|---|
RESULT kind=2 request=1 outcome=2 |
Host | S1F13 established communication. |
RESULT kind=2 request=3 outcome=5 |
Host | S1F3 status values arrived. |
RESULT kind=1 request=0 outcome=13 |
Equipment | S1F3 arrived and GEM replied automatically. |
RESULT kind=3 request=3 outcome=0 |
Host | S1F3 ended without a reply, for example after T3. |
Request kinds 1–7 are listed below. Values 8 and higher cover reports, alarms, remote commands, process programs, and spooling; see GemRequestKind for the full list.
| Value | Request | Message |
|---|---|---|
| 1 | Establish |
S1F13 |
| 2 | AreYouOnline |
S1F1 |
| 3 | StatusValues |
S1F3 |
| 4 | StatusNames |
S1F11 |
| 5 | Offline |
S1F15 |
| 6 | Online |
S1F17 |
| 7 | OperatorOnline |
Operator switch to online on the equipment |
Outcomes 0–13 are listed below. For values 14 and higher, see GemOutcome.
| Value | Outcome | Value | Outcome |
|---|---|---|---|
| 0 | None |
7 | OfflineAccepted |
| 1 | Replied |
8 | OnlineAccepted |
| 2 | CommunicationEstablished |
9 | AlreadyOnline |
| 3 | CommunicationRejected |
10 | OnlineRejected |
| 4 | EquipmentIdentity |
11 | OfflineAck |
| 5 | ValuesReceived |
12 | OnlineAck |
| 6 | NamesReceived |
13 | StatusReplied |
The tutorial peers don’t print code for a Failed result. To see the reason, look for the transaction_failed record in the diagnostics log.
HSMS events
Section titled “HSMS events”Events you take from a TCP endpoint (pdcomm_tcp_next_event) have one of these kinds. The .NET type is HsmsEventKind.
| Value | Event | Meaning |
|---|---|---|
| 1 | STATE_CHANGED |
The connection state changed. See from_state and to_state. |
| 2 | RECEIVED |
A message arrived. is_reply is 1 for a reply, and aborted is 1 for an F0 reply. |
| 3 | FAILED |
A request ended without a reply. code is a failure reason from the next table. |
| 4 | PROTOCOL_ERROR |
Received data broke the protocol. code is a protocol error from the table below. |
| 5 | TRANSPORT_ERROR |
A socket operation failed. code is the operation and detail is the I/O error kind. |
| 6 | W0_WRITTEN |
A W0 message was written completely. This doesn’t prove that the peer received it. |
Failure reasons
Section titled “Failure reasons”The .NET type is HsmsFailureReason.
| Value | Reason | Meaning |
|---|---|---|
| 1 | Cancelled |
Your application cancelled the request or stopped the endpoint. |
| 2 | T3Timeout |
No reply to a data message arrived within T3. The connection stays up. |
| 3 | T6Timeout |
No reply to Select, Deselect, or Linktest arrived within T6. The connection drops next. |
| 4 | Disconnected |
The connection dropped while the request waited for a reply. |
| 5 | Deselected |
The peer ended communication with Deselect. |
| 6 | Rejected |
The peer rejected the message with Reject.req. value1 is the rejected type and value2 is the reason code. |
| 7 | SelectStatus |
Select.rsp carried a nonzero status. value1 is the status. |
| 8 | DeselectStatus |
Deselect.rsp carried a nonzero status. value1 is the status. |
Protocol errors
Section titled “Protocol errors”The .NET type is HsmsProtocolError.
| Value | Error | Meaning |
|---|---|---|
| 1 | InvalidFrame |
The header or body of a received frame is malformed. detail holds the specific reason, 7 to 13 (PType, SType, session ID, header field, control message body, SECS-II body and so on). When the framing of the byte stream itself breaks, such as an invalid length field, the stream can’t be resynchronized, so the endpoint closes the connection with a transport error (Read, InvalidData) instead of this event. |
| 2 | FrameLimit |
Reserved; this version doesn’t produce it. A received frame larger than max_frame_bytes closes the connection with a transport error (Read, InvalidData). An outgoing body that doesn’t fit in a frame makes the request call return PDCOMM_LIMIT_EXCEEDED. |
| 3 | WrongSession |
A data message carried another session ID and was dropped. value1 is the expected ID and value2 is the received ID. |
| 4 | DuplicatePrimary |
A request with the same system bytes is still open. |
| 5 | IncomingLimit |
A W1 request arrived while all 32 incoming slots held W1 requests that still wait for your reply. The SDK answers the new request with F0 automatically. |
| 6 | UnexpectedReply |
A reply arrived with no open transaction, typically a late reply after T3. |
| 7 | UnexpectedControl |
Reserved; this version doesn’t produce it. An unmatched control reply is answered with Reject.req (reason 3) instead of an event. |
| 8 | InvalidReject |
A Reject.req arrived with no matching transaction. |
| 9 | InvalidReply |
Reserved; this version doesn’t produce it. |
Transport errors
Section titled “Transport errors”code is the operation that failed (HsmsIoOperation): 1 Bind, 2 LocalAddress, 3 Connect, 4 Configure, 5 Accept, 6 Read, 7 Write.
detail is the I/O error kind (HsmsIoErrorKind). These are the values you see most often:
| Value | Kind | Typical situation |
|---|---|---|
| 3 | Refused |
Nothing listens on the port the active side connects to. |
| 4 | Reset |
The peer reset the connection. |
| 7 | AddressInUse |
Another program already uses the address and port. |
| 8 | AddressUnavailable |
The IP address doesn’t belong to this computer. |
| 11 | TimedOut |
A connect or write didn’t finish in time. |
| 13 | UnexpectedEof |
The peer closed the connection. |
| 15 | InvalidData |
The connection closed in the middle of a frame, or the endpoint closed it because a received frame had an invalid length field or exceeded max_frame_bytes. |
The full list is 0 Other, 1 NotFound, 2 Permission, 3 Refused, 4 Reset, 5 Aborted, 6 NotConnected, 7 AddressInUse, 8 AddressUnavailable, 9 BrokenPipe, 10 WouldBlock, 11 TimedOut, 12 Interrupted, 13 UnexpectedEof, 14 InvalidInput, 15 InvalidData, and 16 WriteZero. The SDK handles WouldBlock internally, so your application never sees it.
Unhandled messages and S9
Section titled “Unhandled messages and S9”The PDComm SDK never sends S9Fx error messages on its own; the PDComm simulator behaves differently, as How the simulator answers at the end of this section explains. When the peer sends an S9Fx message, your application gets an Unhandled result with code 3 (Unsupported).
When a received W1 request is Unhandled, the SDK sends no reply, so your application must answer it:
- Reply with
pdcomm_tcp_reply(.NET:GemEndpoint.ReplyRaw) on the original event, or abort the transaction with function 0 (SxF0). - If you can’t reply, close the endpoint.
If you don’t reply, two things go wrong:
- The peer waits until T3 expires and then fails the request.
- The unanswered W1 request keeps holding an incoming slot. Once 32 slots are taken, the SDK answers every further W1 request with an automatic F0 and reports an
IncomingLimitprotocol error. From then on, even normal requests such as S1F3 fail.
The tutorial peers abort every W1 request they don’t handle with F0. When the C# host sends alarms (S5F5) to the C++ tutorial equipment, which has no alarm catalog, the equipment prints this:
RESULT kind=5 request=0 outcome=0SECS-II 01 00ABORT S5F0 for S5F5The host prints this:
RESULT kind=3 request=13 outcome=0 status=NoneThe host’s result is Failed with code 12 (Aborted), and it arrives right away instead of after T3.
How the simulator answers
Section titled “How the simulator answers”The PDComm simulator answers unhandled primary messages itself, as SEMI E5 Stream 9 describes. While an Equipment-role session is communicating, it answers as follows:
| Received primary | Simulator’s answer |
|---|---|
| A stream the equipment doesn’t recognize | S9F3 (Unrecognized Stream Type) |
| An unrecognized function in a known stream | S9F5 (Unrecognized Function Type) |
| A message with a malformed body | S9F7 (Illegal Data) |
A known message it can’t serve right now, such as S5F5 without an alarm catalog |
SxF0 only |
Each S9Fx carries the 10-byte header of the received message (MHEAD) and has no W-bit. When the received primary has its W-bit set, the simulator also closes the transaction with function 0 of the same stream (SxF0), so the peer doesn’t wait for T3 and no incoming slot stays taken. A Host-role session, or a session that isn’t communicating yet, sends no S9Fx and answers W-bit requests with SxF0 only.