/* scada.c — simulates a SCADA control center */ #include #include #include #include #include #include "cs104_connection.h" #include "hal_thread.h" #include "hal_time.h" static bool running = true; void sigint_handler(int s) { running = false; } /* ── called for every ASDU received from RTU ───────────────── */ static bool asduReceivedHandler(void* p, int address, CS101_ASDU asdu) { int typeId = CS101_ASDU_getTypeID(asdu); int cot = CS101_ASDU_getCOT(asdu); /* measured float value M_ME_NC_1 */ if (typeId == M_ME_NC_1) { int i; for (i = 0; i < CS101_ASDU_getNumberOfElements(asdu); i++) { MeasuredValueShort mv = (MeasuredValueShort) CS101_ASDU_getElement(asdu, i); int ioa = InformationObject_getObjectAddress((InformationObject)mv); float value = MeasuredValueShort_getValue(mv); InformationObject_destroy((InformationObject)mv); if (ioa == 100) printf("[SCADA ←] Voltage IOA=100 %.2f kV\n", value); else if (ioa == 101) printf("[SCADA ←] Current IOA=101 %.1f A\n", value); else printf("[SCADA ←] Float IOA=%d %.2f\n", ioa, value); } } /* single point M_SP_NA_1 */ else if (typeId == M_SP_NA_1) { int i; for (i = 0; i < CS101_ASDU_getNumberOfElements(asdu); i++) { SinglePointInformation sp = (SinglePointInformation) CS101_ASDU_getElement(asdu, i); int ioa = InformationObject_getObjectAddress((InformationObject)sp); bool state = SinglePointInformation_getValue(sp); InformationObject_destroy((InformationObject)sp); if (ioa == 200) printf("[SCADA ←] Breaker1 IOA=200 %s\n", state ? "ON" : "OFF"); else if (ioa == 201) printf("[SCADA ←] Breaker2 IOA=201 %s\n", state ? "ON" : "OFF"); else printf("[SCADA ←] Point IOA=%d %s\n", ioa, state ? "ON" : "OFF"); } } /* command confirmation */ else if (typeId == C_SC_NA_1) { if (cot == CS101_COT_ACTIVATION_CON) printf("[SCADA ←] Command confirmed by RTU\n"); else if (cot == CS101_COT_UNKNOWN_IOA) printf("[SCADA ←] Command rejected — unknown IOA\n"); } else { printf("[SCADA ←] TypeID=%d COT=%d\n", typeId, cot); } return true; } /* ── connection state ───────────────────────────────────────── */ static void connectionHandler(void* p, CS104_Connection con, CS104_ConnectionEvent event) { if (event == CS104_CONNECTION_OPENED) printf("[SCADA] Connected to RTU\n"); else if (event == CS104_CONNECTION_CLOSED) printf("[SCADA] Disconnected from RTU\n"); else if (event == CS104_CONNECTION_STARTDT_CON_RECEIVED) printf("[SCADA] STARTDT confirmed — data transfer active\n"); else if (event == CS104_CONNECTION_STOPDT_CON_RECEIVED) printf("[SCADA] STOPDT confirmed\n"); } /* ── send a single command to RTU ───────────────────────────── */ void send_command(CS104_Connection con, int ioa, bool state) { CS101_AppLayerParameters alParams = CS104_Connection_getAppLayerParameters(con); CS101_ASDU asdu = CS101_ASDU_create(alParams, false, CS101_COT_ACTIVATION, 0, 1, false, false); InformationObject io = (InformationObject) SingleCommand_create(NULL, ioa, state, false, 0); CS101_ASDU_addInformationObject(asdu, io); InformationObject_destroy(io); CS104_Connection_sendASDU(con, asdu); CS101_ASDU_destroy(asdu); printf("[SCADA →] Command sent — IOA=%d state=%s\n", ioa, state ? "ON" : "OFF"); } /* ── send general interrogation ─────────────────────────────── */ void send_gi(CS104_Connection con) { CS104_Connection_sendInterrogationCommand(con, CS101_COT_ACTIVATION, 1, IEC60870_QOI_STATION); printf("[SCADA →] General Interrogation sent\n"); } /* ── main ───────────────────────────────────────────────────── */ int main(int argc, char** argv) { signal(SIGINT, sigint_handler); const char* ip = (argc > 1) ? argv[1] : "127.0.0.1"; printf("[SCADA] Connecting to RTU at %s:2404\n\n", ip); CS104_Connection con = CS104_Connection_create(ip, 2404); CS104_Connection_setConnectionHandler(con, connectionHandler, NULL); CS104_Connection_setASDUReceivedHandler(con, asduReceivedHandler, NULL); CS104_Connection_connect(con); Thread_sleep(1000); /* start data transfer */ CS104_Connection_sendStartDT(con); Thread_sleep(500); /* request all current values */ send_gi(con); Thread_sleep(2000); /* main loop — send commands periodically */ uint64_t lastCmd = 0; bool b1_state = false; bool b2_state = false; printf("\n[SCADA] Running — commands every 5s, Ctrl+C to stop\n\n"); while (running) { uint64_t now = Hal_getMonotonicTimeInMs(); /* every 5s — toggle breaker1 then breaker2 alternately */ if (now >= lastCmd + 5000) { lastCmd = now; static int cmd_count = 0; cmd_count++; if (cmd_count % 2 == 0) { b1_state = !b1_state; send_command(con, 200, b1_state); /* toggle breaker1 */ } else { b2_state = !b2_state; send_command(con, 201, b2_state); /* toggle breaker2 */ } } Thread_sleep(10); } printf("\n[SCADA] Shutting down\n"); CS104_Connection_sendStopDT(con); Thread_sleep(500); CS104_Connection_destroy(con); return 0; }