Skip to content

SEMI E4 · E5 · E30 · E37

SECS/GEM

Build GEM equipment and hosts that communicate over HSMS and SECS-I, with SECS-II messages and the GEM capabilities for both sides.

What it does

  • HSMS-SS (SEMI E37, E37.1) over TCP/IP, active or passive, with the T3, T5, T6, T7 and T8 timers you set.
  • SECS-I (SEMI E4) through your read and write callbacks, so the same code drives a COM port, a virtual serial port or an in-memory line.
  • GEM communication and control state models (S1F13, S1F15, S1F17).
  • Data collection: status and data variables, event reports (S2F33, S2F35, S2F37, S6F11) and equipment constants.
  • Alarms (S5F1–S5F8) and remote commands (S2F41, S2F49) checked against your command catalog.
  • Process programs (S7F1–S7F6, S7F17–S7F32) and spooling (S2F43, S6F23).
  • A C ABI that creates no threads of its own; .NET worker classes run the poll loop for you.

Example

Each binding shows code from a real example file in the examples folder of the PDComm SDK. The source file and line range appear above the code.

SECS/GEM host and equipment in C

Source: examples/c/gem.c:181-222
while (!done && !failed && time(NULL) < deadline) {
    for (int side = 0; side < 2; ++side) {
        size_t progressed = 0;
        failed |= !ok(pdcomm_tcp_poll(endpoints[side], &progressed), "pdcomm_tcp_poll");
        while (!failed) {
            PdcommTcpEvent *event = NULL;
            failed |= !ok(pdcomm_tcp_next_event(endpoints[side], &event), "pdcomm_tcp_next_event");
            if (!event) break;
            PdcommGemResult *result = NULL;
            PdcommGemResultInfo info = {0};
            info.struct_size = sizeof(info);
            if (ok(pdcomm_gem_handle_event(endpoints[side], event, &result), "pdcomm_gem_handle_event") &&
                ok(pdcomm_gem_result_info(result, &info), "pdcomm_gem_result_info") &&
                endpoints[side] == host && info.kind == PDCOMM_GEM_RESPONSE) {
                /* Each reply of the host's requests leads to the next request. */
                if (info.outcome == PDCOMM_GEM_COMMUNICATION_ESTABLISHED) {
                    puts("S1F14: communication established");
                    failed |= !ok(pdcomm_gem_host_request(host, PDCOMM_GEM_ARE_YOU_ONLINE, NULL, 0, &token),
                                  "pdcomm_gem_host_request");
                } else if (info.outcome == PDCOMM_GEM_EQUIPMENT_IDENTITY) {
                    puts("S1F2: equipment identity received");
                    failed |= !ok(pdcomm_gem_host_request(host, PDCOMM_GEM_STATUS_VALUES, ids, 2, &token),
                                  "pdcomm_gem_host_request");
                } else if (info.outcome == PDCOMM_GEM_VALUES_RECEIVED) {
                    for (size_t index = 0; index < info.entry_count; ++index) print_status_value(result, index);
                    done = info.entry_count == 2;
                    failed |= !done;
                } else {
                    fprintf(stderr, "Unexpected outcome %u\n", (unsigned)info.outcome);
                    failed = 1;
                }
            }
            pdcomm_gem_result_destroy(result);
            pdcomm_tcp_event_destroy(event);
        }
    }
    if (!began && tcp_state(host, NULL) == PDCOMM_TCP_SELECTED) {
        began = 1;
        failed |= !ok(pdcomm_gem_begin_communication(host, &token), "pdcomm_gem_begin_communication");
    }
    pause_one_millisecond();
}

From the C example: one thread polls the equipment and host endpoints and hands every event to pdcomm_gem_handle_event(). The host establishes communication (S1F13), asks for the equipment identity (S1F1), then reads two status values (S1F3).