/* rtu.c — simulates a substation RTU */ #include #include #include #include #include "cs104_slave.h" #include "hal_thread.h" #include "hal_time.h" static bool running = true; static float voltage = 11.2; /* kV */ static float current = 450.0; /* A */ static bool breaker1 = false; /* OFF */ static bool breaker2 = false; /* OFF */ void sigint_handler(int s) { running = false; } /* ── connection events ──────────────────────────────────────── */ static void connectionEventHandler(void* p, IMasterConnection con, CS104_PeerConnectionEvent event) { if (event == CS104_CON_EVENT_CONNECTION_OPENED) printf("[RTU] SCADA connected\n"); else if (event == CS104_CON_EVENT_CONNECTION_CLOSED) printf("[RTU] SCADA disconnected\n"); else if (event == CS104_CON_EVENT_ACTIVATED) printf("[RTU] Data transfer started\n"); } /* ── accept all connections ─────────────────────────────────── */ static bool connectionRequestHandler(void* p, const char* ip) { printf("[RTU] Connection request from %s — accepted\n", ip); return true; } /* ── send all current values (General Interrogation) ────────── */ static bool interrogationHandler(void* p, IMasterConnection con, CS101_ASDU asdu, uint8_t qoi) { printf("[RTU] General Interrogation received\n"); CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(con); /* confirm GI received */ IMasterConnection_sendACT_CON(con, asdu, false); /* send voltage IOA 100 */ CS101_ASDU a1 = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); InformationObject io1 = (InformationObject) MeasuredValueShort_create(NULL, 100, voltage, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(a1, io1); InformationObject_destroy(io1); IMasterConnection_sendASDU(con, a1); CS101_ASDU_destroy(a1); /* send current IOA 101 */ CS101_ASDU a2 = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); InformationObject io2 = (InformationObject) MeasuredValueShort_create(NULL, 101, current, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(a2, io2); InformationObject_destroy(io2); IMasterConnection_sendASDU(con, a2); CS101_ASDU_destroy(a2); /* send breaker1 IOA 200 */ CS101_ASDU a3 = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); InformationObject io3 = (InformationObject) SinglePointInformation_create(NULL, 200, breaker1, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(a3, io3); InformationObject_destroy(io3); IMasterConnection_sendASDU(con, a3); CS101_ASDU_destroy(a3); /* send breaker2 IOA 201 */ CS101_ASDU a4 = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); InformationObject io4 = (InformationObject) SinglePointInformation_create(NULL, 201, breaker2, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(a4, io4); InformationObject_destroy(io4); IMasterConnection_sendASDU(con, a4); CS101_ASDU_destroy(a4); /* GI complete */ IMasterConnection_sendACT_TERM(con, asdu); printf("[RTU] GI complete — sent voltage=%.1f current=%.1f breaker1=%d breaker2=%d\n", voltage, current, breaker1, breaker2); return true; } /* ── handle commands from SCADA ─────────────────────────────── */ static bool asduHandler(void* p, IMasterConnection con, CS101_ASDU asdu) { /* single command C_SC_NA_1 */ if (CS101_ASDU_getTypeID(asdu) == C_SC_NA_1) { if (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION) { InformationObject io = CS101_ASDU_getElement(asdu, 0); if (io) { int ioa = InformationObject_getObjectAddress(io); bool state = SingleCommand_getState((SingleCommand)io); InformationObject_destroy(io); printf("[RTU] Command received — IOA=%d state=%s\n", ioa, state ? "ON" : "OFF"); if (ioa == 200) { breaker1 = state; printf("[RTU] Breaker 1 → %s\n", breaker1 ? "ON" : "OFF"); CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON); } else if (ioa == 201) { breaker2 = state; printf("[RTU] Breaker 2 → %s\n", breaker2 ? "ON" : "OFF"); CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON); } else { CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA); } IMasterConnection_sendASDU(con, asdu); } } return true; } return false; } /* ── main ───────────────────────────────────────────────────── */ int main() { signal(SIGINT, sigint_handler); CS104_Slave slave = CS104_Slave_create(10, 10); CS104_Slave_setLocalAddress(slave, "0.0.0.0"); CS104_Slave_setLocalPort(slave, 2404); CS101_AppLayerParameters alParams = CS104_Slave_getAppLayerParameters(slave); CS104_Slave_setConnectionEventHandler(slave, connectionEventHandler, NULL); CS104_Slave_setConnectionRequestHandler(slave, connectionRequestHandler, NULL); CS104_Slave_setInterrogationHandler(slave, interrogationHandler, NULL); CS104_Slave_setASDUHandler(slave, asduHandler, NULL); CS104_Slave_start(slave); if (!CS104_Slave_isRunning(slave)) { printf("[RTU] Failed to start!\n"); CS104_Slave_destroy(slave); return 1; } printf("[RTU] Running on port 2404\n"); printf("[RTU] IOA 100=voltage 101=current 200=breaker1 201=breaker2\n\n"); uint64_t lastSent = 0; while (running) { uint64_t now = Hal_getMonotonicTimeInMs(); /* every 3s — send spontaneous measurements */ if (now >= lastSent + 3000) { lastSent = now; /* simulate small fluctuations */ voltage += ((rand() % 10) - 5) * 0.1; current += ((rand() % 20) - 10) * 1.0; /* voltage — IOA 100 */ CS101_ASDU a1 = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); InformationObject io1 = (InformationObject) MeasuredValueShort_create(NULL, 100, voltage, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(a1, io1); InformationObject_destroy(io1); CS104_Slave_enqueueASDU(slave, a1); CS101_ASDU_destroy(a1); /* current — IOA 101 */ CS101_ASDU a2 = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); InformationObject io2 = (InformationObject) MeasuredValueShort_create(NULL, 101, current, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(a2, io2); InformationObject_destroy(io2); CS104_Slave_enqueueASDU(slave, a2); CS101_ASDU_destroy(a2); printf("[RTU →] voltage=%.2fkV current=%.1fA breaker1=%s breaker2=%s\n", voltage, current, breaker1 ? "ON" : "OFF", breaker2 ? "ON" : "OFF"); } Thread_sleep(10); } printf("[RTU] Shutting down\n"); CS104_Slave_stop(slave); CS104_Slave_destroy(slave); return 0; }