### Define Installation Directories Source: https://github.com/mz-automation/lib60870/blob/master/lib60870-C/src/hal/CMakeLists.txt Sets installation paths for binaries and libraries, utilizing GNUInstallDirs on Unix systems for multiarch support. ```cmake set(BINDIR "bin") set(LIBDIR "lib") if(UNIX) # GNUInstallDirs is required for Debian multiarch include(GNUInstallDirs) set(LIBDIR ${CMAKE_INSTALL_LIBDIR}) set(BINDIR ${CMAKE_INSTALL_BINDIR}) endif() ``` -------------------------------- ### Install Target Libraries Source: https://github.com/mz-automation/lib60870/blob/master/lib60870-C/src/hal/CMakeLists.txt Defines the installation rules for the hal and hal-shared targets. ```cmake install (TARGETS hal hal-shared RUNTIME DESTINATION ${BINDIR} COMPONENT Applications ARCHIVE DESTINATION ${LIBDIR} COMPONENT Libraries LIBRARY DESTINATION ${LIBDIR} COMPONENT Libraries ) ``` -------------------------------- ### Create and Configure CS104 Slave Source: https://context7.com/mz-automation/lib60870/llms.txt Initializes a CS104 slave instance with specified queue sizes, sets network addresses and port, and configures the server mode. This is the starting point for any CS104 slave implementation. ```c #include "cs104_slave.h" #include "hal_thread.h" #include "hal_time.h" #include #include static bool running = true; void sigint_handler(int signalId) { running = false; } /* Handle incoming interrogation commands */ static bool interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) { CS104_Slave slave = (CS104_Slave)parameter; int ca = CS101_ASDU_getCA(asdu); printf("Received interrogation for CA %i, QOI %i\n", ca, qoi); if (ca == 1 && qoi == IEC60870_QOI_STATION) { /* Send activation confirmation */ IMasterConnection_sendACT_CON(connection, asdu, false); /* Create and send response data */ CS101_AppLayerParameters alParams = CS104_Slave_getAppLayerParameters(slave); CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); InformationObject io = (InformationObject)MeasuredValueScaled_create(NULL, 100, 1234, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(newAsdu, io); CS101_ASDU_addInformationObject(newAsdu, (InformationObject)MeasuredValueScaled_create((MeasuredValueScaled)io, 101, 5678, IEC60870_QUALITY_GOOD)); InformationObject_destroy(io); IMasterConnection_sendASDU(connection, newAsdu); CS101_ASDU_destroy(newAsdu); /* Send activation termination */ newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_ACTIVATION_TERMINATION, 0, 1, false, false); io = (InformationObject)InterrogationCommand_create(NULL, 0, IEC60870_QOI_STATION); CS101_ASDU_addInformationObject(newAsdu, io); InformationObject_destroy(io); IMasterConnection_sendASDU(connection, newAsdu); CS101_ASDU_destroy(newAsdu); } return true; } /* Handle single commands from master */ static bool asduHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu) { if (CS101_ASDU_getTypeID(asdu) == C_SC_NA_1) { InformationObject io = CS101_ASDU_getElement(asdu, 0); if (io) { SingleCommand sc = (SingleCommand)io; printf("Single command received: IOA=%i, State=%i\n", InformationObject_getObjectAddress(io), SingleCommand_getState(sc)); CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON); InformationObject_destroy(io); } IMasterConnection_sendASDU(connection, asdu); return true; } return false; } int main(void) { signal(SIGINT, sigint_handler); /* Create slave with queue sizes (low priority, high priority) */ CS104_Slave slave = CS104_Slave_create(100, 100); CS104_Slave_setLocalAddress(slave, "0.0.0.0"); CS104_Slave_setLocalPort(slave, IEC_60870_5_104_DEFAULT_PORT); CS104_Slave_setServerMode(slave, CS104_MODE_SINGLE_REDUNDANCY_GROUP); /* Register callback handlers */ CS104_Slave_setInterrogationHandler(slave, interrogationHandler, slave); CS104_Slave_setASDUHandler(slave, asduHandler, NULL); CS104_Slave_start(slave); if (!CS104_Slave_isRunning(slave)) { printf("Failed to start server!\n"); return 1; } int16_t scaledValue = 0; uint64_t lastSent = 0; while (running) { uint64_t now = Hal_getTimeInMs(); /* Send periodic measurements every 2 seconds */ if (now >= lastSent + 2000) { CS101_AppLayerParameters alParams = CS104_Slave_getAppLayerParameters(slave); CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); InformationObject io = (InformationObject)MeasuredValueScaled_create(NULL, 110, scaledValue++, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(newAsdu, io); InformationObject_destroy(io); CS104_Slave_enqueueASDU(slave, newAsdu); CS101_ASDU_destroy(newAsdu); lastSent = now; } Thread_sleep(10); } CS104_Slave_stop(slave); CS104_Slave_destroy(slave); return 0; } ``` -------------------------------- ### Start CS104 Slave Server Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Initiates the CS104 slave server, starting a background thread to listen for incoming client connections. ```c CS104_Slave_start(slave); ``` -------------------------------- ### Configure CMake for lib60870 Master Example Source: https://github.com/mz-automation/lib60870/blob/master/lib60870-C/examples/cs101_master_unbalanced/CMakeLists.txt Defines the source files and links the lib60870 library. Includes a conditional check to set the source language to CXX on Windows platforms. ```cmake include_directories( . ) set(example_SRCS master_example.c ) IF(WIN32) set_source_files_properties(${example_SRCS} PROPERTIES LANGUAGE CXX) ENDIF(WIN32) add_executable(cs101_master_unbalanced ${example_SRCS} ) target_link_libraries(cs101_master_unbalanced lib60870 ) ``` -------------------------------- ### Create and Send Control Commands in C Source: https://context7.com/mz-automation/lib60870/llms.txt Demonstrates the creation and sending of various IEC 60870-5-104 control commands including SingleCommand, DoubleCommand, StepCommand, and SetpointCommands. Includes examples for direct execution, select-before-operate, pulse durations, and commands with timestamps. ```c #include "cs104_connection.h" #include "cs101_information_objects.h" #include void send_control_commands(CS104_Connection con) { /* Single command - direct execute, no pulse */ SingleCommand sc1 = SingleCommand_create(NULL, 5000, true, false, IEC60870_QOC_NO_ADDITIONAL_DEFINITION); printf("Single Command: IOA=%i, State=%s, Select=%s, QU=%i\n", InformationObject_getObjectAddress((InformationObject)sc1), SingleCommand_getState(sc1) ? "ON" : "OFF", SingleCommand_isSelect(sc1) ? "yes" : "no", SingleCommand_getQU(sc1)); CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, (InformationObject)sc1); SingleCommand_destroy(sc1); /* Single command with select-before-operate (SBO) */ SingleCommand sc2 = SingleCommand_create(NULL, 5001, false, true, IEC60870_QOC_SHORT_PULSE_DURATION); CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, (InformationObject)sc2); SingleCommand_destroy(sc2); /* Double command - turn ON with long pulse */ DoubleCommand dc = DoubleCommand_create(NULL, 5010, IEC60870_DOUBLE_POINT_ON, false, IEC60870_QOC_LONG_PULSE_DURATION); printf("Double Command: IOA=%i, State=%i, QU=%i\n", InformationObject_getObjectAddress((InformationObject)dc), DoubleCommand_getState(dc), DoubleCommand_getQU(dc)); CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, (InformationObject)dc); DoubleCommand_destroy(dc); /* Step command - raise position */ StepCommand stc = StepCommand_create(NULL, 5020, IEC60870_STEP_HIGHER, false, 0); CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, (InformationObject)stc); StepCommand_destroy(stc); /* Setpoint command - normalized value (-1.0 to 1.0) */ SetpointCommandNormalized spn = SetpointCommandNormalized_create(NULL, 5030, 0.75f, false, 0); printf("Setpoint Normalized: IOA=%i, Value=%.2f\n", InformationObject_getObjectAddress((InformationObject)spn), SetpointCommandNormalized_getValue(spn)); CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, (InformationObject)spn); SetpointCommandNormalized_destroy(spn); /* Setpoint command - scaled value (-32768 to 32767) */ SetpointCommandScaled sps = SetpointCommandScaled_create(NULL, 5040, 15000, false, 0); CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, (InformationObject)sps); SetpointCommandScaled_destroy(sps); /* Setpoint command - short floating point */ SetpointCommandShort spf = SetpointCommandShort_create(NULL, 5050, 123.456f, false, 0); CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, (InformationObject)spf); SetpointCommandShort_destroy(spf); /* Command with timestamp (C_SC_TA_1) */ struct sCP56Time2a timestamp; CP56Time2a_createFromMsTimestamp(×tamp, Hal_getTimeInMs()); SingleCommandWithCP56Time2a sct = SingleCommandWithCP56Time2a_create(NULL, 5060, true, false, 0, ×tamp); CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, (InformationObject)sct); SingleCommandWithCP56Time2a_destroy(sct); } ``` -------------------------------- ### Install ASDU Received Handler Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Register the callback handler for processing received ASDUs. ```c CS101_Master_setASDUReceivedHandler(master, asduReceivedHandler, NULL); ``` -------------------------------- ### Create Measured Values in C Source: https://context7.com/mz-automation/lib60870/llms.txt Demonstrates the creation of different types of measured value information objects, including scaled, normalized, short floating point, and integrated totals. Includes examples with timestamps and quality flags. Ensure necessary headers are included. ```c #include "cs101_information_objects.h" #include "iec60870_common.h" #include void create_measured_values(CS101_AppLayerParameters alParams) { /* Scaled measured value without timestamp */ MeasuredValueScaled mvs = MeasuredValueScaled_create(NULL, 1000, 2500, IEC60870_QUALITY_GOOD); printf("Scaled Value: IOA=%i, Value=%i, Quality=0x%02X\n", InformationObject_getObjectAddress((InformationObject)mvs), MeasuredValueScaled_getValue(mvs), MeasuredValueScaled_getQuality(mvs)); MeasuredValueScaled_destroy(mvs); /* Normalized measured value (-1.0 to 1.0 range) */ MeasuredValueNormalized mvn = MeasuredValueNormalized_create(NULL, 1010, 0.85f, IEC60870_QUALITY_GOOD); printf("Normalized Value: IOA=%i, Value=%.4f\n", InformationObject_getObjectAddress((InformationObject)mvn), MeasuredValueNormalized_getValue(mvn)); MeasuredValueNormalized_destroy(mvn); /* Short floating point measured value */ MeasuredValueShort mvf = MeasuredValueShort_create(NULL, 1020, 123.456f, IEC60870_QUALITY_GOOD); printf("Float Value: IOA=%i, Value=%.3f\n", InformationObject_getObjectAddress((InformationObject)mvf), MeasuredValueShort_getValue(mvf)); MeasuredValueShort_destroy(mvf); /* Measured value with CP56Time2a timestamp */ struct sCP56Time2a timestamp; CP56Time2a_createFromMsTimestamp(×tamp, Hal_getTimeInMs()); MeasuredValueScaledWithCP56Time2a mvst = MeasuredValueScaledWithCP56Time2a_create(NULL, 1030, 5000, IEC60870_QUALITY_GOOD, ×tamp); printf("Scaled with Timestamp: IOA=%i, Value=%i, Time=%02i:%02i:%02i\n", InformationObject_getObjectAddress((InformationObject)mvst), MeasuredValueScaled_getValue((MeasuredValueScaled)mvst), CP56Time2a_getHour(×tamp), CP56Time2a_getMinute(×tamp), CP56Time2a_getSecond(×tamp)); MeasuredValueScaledWithCP56Time2a_destroy(mvst); /* Value with quality flags */ QualityDescriptor quality = IEC60870_QUALITY_SUBSTITUTED | IEC60870_QUALITY_NON_TOPICAL; MeasuredValueScaled mvq = MeasuredValueScaled_create(NULL, 1040, -1000, quality); printf("Value with Quality Flags: Value=%i, Quality=0x%02X (Substituted=%s, NonTopical=%s)\n", MeasuredValueScaled_getValue(mvq), MeasuredValueScaled_getQuality(mvq), (quality & IEC60870_QUALITY_SUBSTITUTED) ? "yes" : "no", (quality & IEC60870_QUALITY_NON_TOPICAL) ? "yes" : "no"); MeasuredValueScaled_destroy(mvq); /* Integrated totals (counter values) */ struct sBinaryCounterReading bcr; BinaryCounterReading_create(&bcr, 123456789, 5, false, false, false); IntegratedTotals it = IntegratedTotals_create(NULL, 1050, &bcr); BinaryCounterReading bcrResult = IntegratedTotals_getBCR(it); printf("Integrated Total: IOA=%i, Counter=%i, SeqNum=%i\n", InformationObject_getObjectAddress((InformationObject)it), BinaryCounterReading_getValue(bcrResult), BinaryCounterReading_getSequenceNumber(bcrResult)); IntegratedTotals_destroy(it); } ``` -------------------------------- ### Configure TLS Client Build Source: https://github.com/mz-automation/lib60870/blob/master/lib60870-C/examples/tls_client/CMakeLists.txt This CMake script configures the build for a TLS client example. It sets include directories, defines source files, and links the executable against the lib60870 library. It also handles platform-specific settings for Windows. ```cmake include_directories( .) set(example_SRCS tls_client.c ) IF(WIN32) set_source_files_properties(${example_SRCS} PROPERTIES LANGUAGE CXX) ENDIF(WIN32) configure_file(client_CA1_1.key client_CA1_1.key COPYONLY) configure_file(client_CA1_1.pem client_CA1_1.pem COPYONLY) configure_file(root_CA1.pem root_CA1.pem COPYONLY) configure_file(server_CA1_1.pem server_CA1_1.pem COPYONLY) add_executable(tls_client ${example_SRCS} ) target_link_libraries(tls_client lib60870 ) ``` -------------------------------- ### Server - CS104 Configuration Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Configuration and setup of an IEC 60870-5-104 server/slave. ```APIDOC ## POST /api/server/cs104/config ### Description Configures and sets up an IEC 60870-5-104 server/slave instance. ### Method POST ### Endpoint /api/server/cs104/config ### Parameters #### Request Body - **port** (int) - Required - The port number for the server. - **max_connections** (int) - Required - The maximum number of allowed connections. ### Request Example ```c CS104_Slave slave = CS104_Slave_create(100, 100); // Further configuration can be applied here ``` ### Response #### Success Response (200) Indicates the server configuration was successful. #### Response Example (No specific response body defined in the documentation) ``` -------------------------------- ### Create CS104 Slave with Redundancy Groups Source: https://context7.com/mz-automation/lib60870/llms.txt Initializes a CS104 slave in multiple redundancy groups mode and configures its local address. This is the starting point for managing redundant connections. ```c #include "cs104_slave.h" #include "hal_thread.h" #include #include static bool running = true; void sigint_handler(int signalId) { running = false; } int main(void) { signal(SIGINT, sigint_handler); /* Create slave with multiple redundancy groups mode */ CS104_Slave slave = CS104_Slave_create(100, 100); CS104_Slave_setLocalAddress(slave, "0.0.0.0"); CS104_Slave_setServerMode(slave, CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS); ``` -------------------------------- ### Start CS104 Slave and Enqueue ASDUs Source: https://context7.com/mz-automation/lib60870/llms.txt Starts the CS104 slave and enters a loop to periodically enqueue ASDUs containing measured values. ASDUs enqueued here will be distributed to all connected clients in their respective redundancy groups. ```c CS104_Slave_start(slave); if (CS104_Slave_isRunning(slave)) { printf("Server with redundancy groups started\n"); CS101_AppLayerParameters alParams = CS104_Slave_getAppLayerParameters(slave); int16_t value = 0; while (running) { /* Enqueued ASDUs will be delivered to all connected clients in their respective redundancy groups */ CS101_ASDU asdu = CS101_ASDU_create(alParams, false, CS101_COT_SPONTANEOUS, 0, 1, false, false); InformationObject io = (InformationObject)MeasuredValueScaled_create(NULL, 100, value++, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(asdu, io); InformationObject_destroy(io); CS104_Slave_enqueueASDU(slave, asdu); CS101_ASDU_destroy(asdu); printf("Queue entries: %i\n", CS104_Slave_getNumberOfQueueEntries(slave, NULL)); Thread_sleep(5000); } } ``` -------------------------------- ### Initialize CS101 Master Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Create a new master instance and configure the slave address and ASDU handler. ```c CS101_Master master = CS101_Master_create(port, NULL, NULL, IEC60870_LINK_LAYER_BALANCED); CS101_Master_useSlaveAddress(master, 3); CS101_Master_setASDUReceivedHandler(master, asduReceivedHandler, NULL); ``` -------------------------------- ### Create a CS 104 slave with TLS support Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Initializes a TLS configuration object and applies security settings before creating a secure CS 104 slave instance. ```c TLSConfiguration tlsConfig = TLSConfiguration_create(); TLSConfiguration_setChainValidation(tlsConfig, false); TLSConfiguration_setAllowOnlyKnownCertificates(tlsConfig, true); TLSConfiguration_setOwnKeyFromFile(tlsConfig, "server-key.pem", NULL); TLSConfiguration_setOwnCertificateFromFile(tlsConfig, "server.cer"); TLSConfiguration_addCACertificateFromFile(tlsConfig, "root.cer"); TLSConfiguration_addAllowedCertificateFromFile(tlsConfig, "client1.cer"); /* create a new slave/server instance */ CS104_Slave slave = CS104_Slave_createSecure(100, 100, tlsConfig); ``` -------------------------------- ### Create CS104 Slave Instance Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Initializes an IEC 60870-5-104 server/slave instance with specified buffer sizes. ```c CS104_Slave slave = CS104_Slave_create(100, 100); ``` -------------------------------- ### Demonstrate CP56Time2a and related timestamp operations Source: https://context7.com/mz-automation/lib60870/llms.txt Demonstrates creating timestamps from system time, manual component configuration, and usage of CP24Time2a and CP16Time2a structures. ```c #include "iec60870_common.h" #include "hal_time.h" #include void demonstrate_timestamps(void) { /* Create timestamp from current system time */ struct sCP56Time2a timestamp; uint64_t currentMs = Hal_getTimeInMs(); CP56Time2a_createFromMsTimestamp(×tamp, currentMs); printf("Current Timestamp:\n"); printf(" Date: %04i-%02i-%02i (Day of Week: %i)\n", CP56Time2a_getYear(×tamp) + 2000, CP56Time2a_getMonth(×tamp), CP56Time2a_getDayOfMonth(×tamp), CP56Time2a_getDayOfWeek(×tamp)); printf(" Time: %02i:%02i:%02i.%03i\n", CP56Time2a_getHour(×tamp), CP56Time2a_getMinute(×tamp), CP56Time2a_getSecond(×tamp), CP56Time2a_getMillisecond(×tamp)); printf(" Flags: Invalid=%s, Substituted=%s, SummerTime=%s\n", CP56Time2a_isInvalid(×tamp) ? "yes" : "no", CP56Time2a_isSubstituted(×tamp) ? "yes" : "no", CP56Time2a_isSummerTime(×tamp) ? "yes" : "no"); /* Convert back to milliseconds timestamp */ uint64_t convertedMs = CP56Time2a_toMsTimestamp(×tamp); printf(" Original ms: %lu, Converted ms: %lu\n", (unsigned long)currentMs, (unsigned long)convertedMs); /* Manually set timestamp components */ struct sCP56Time2a manualTime; CP56Time2a_setYear(&manualTime, 24); /* 2024 */ CP56Time2a_setMonth(&manualTime, 6); /* June */ CP56Time2a_setDayOfMonth(&manualTime, 15); CP56Time2a_setDayOfWeek(&manualTime, 6); /* Saturday */ CP56Time2a_setHour(&manualTime, 14); CP56Time2a_setMinute(&manualTime, 30); CP56Time2a_setSecond(&manualTime, 45); CP56Time2a_setMillisecond(&manualTime, 500); CP56Time2a_setInvalid(&manualTime, false); CP56Time2a_setSummerTime(&manualTime, true); printf("\nManual Timestamp: %04i-%02i-%02i %02i:%02i:%02i.%03i (Summer Time)\n", CP56Time2a_getYear(&manualTime) + 2000, CP56Time2a_getMonth(&manualTime), CP56Time2a_getDayOfMonth(&manualTime), CP56Time2a_getHour(&manualTime), CP56Time2a_getMinute(&manualTime), CP56Time2a_getSecond(&manualTime), CP56Time2a_getMillisecond(&manualTime)); /* Short time formats */ struct sCP24Time2a time24; CP24Time2a_setMinute(&time24, 45); CP24Time2a_setSecond(&time24, 30); CP24Time2a_setMillisecond(&time24, 250); printf("\nCP24Time2a: %02i:%02i.%03i\n", CP24Time2a_getMinute(&time24), CP24Time2a_getSecond(&time24), CP24Time2a_getMillisecond(&time24)); /* Elapsed time (CP16Time2a) */ struct sCP16Time2a elapsed; CP16Time2a_setEplapsedTimeInMs(&elapsed, 12500); printf("CP16Time2a (elapsed): %i ms\n", CP16Time2a_getEplapsedTimeInMs(&elapsed)); } ``` -------------------------------- ### Create endpoint client and server certificates Source: https://github.com/mz-automation/lib60870/blob/master/lib60870-C/tests/certs/README.md Commands to generate a private key, create a certificate signing request, and sign it using the root CA. ```bash openssl genrsa -out server_CA1_2.key 2048 openssl req -new -key server_CA1_1.key -out server_CA1_1.csr openssl x509 -req -in server_CA1_1.csr -CA root_CA1.pem -CAkey root_CA1.key -CAcreateserial -out server_CA1_1.pem ``` -------------------------------- ### Build with mbedtls 3.6 using make Source: https://github.com/mz-automation/lib60870/blob/master/lib60870-C/dependencies/README.md Invokes the make command with the specific parameter to enable mbedtls 3.6 support. ```bash make WITH_MBEDTLS3=1 ``` -------------------------------- ### Build with mbedtls 2.28 using make Source: https://github.com/mz-automation/lib60870/blob/master/lib60870-C/dependencies/README.md Invokes the make command with the specific parameter to enable mbedtls 2.28 support. ```bash make WITH_MBEDTLS=1 ``` -------------------------------- ### Configure CS104 Server Settings Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Functions to set server redundancy modes, connection limits, and network binding. ```c CS104_Slave_setServerMode(slave, CS104_MODE_CONNECTION_IS_REDUNDANCY_GROUP); ``` ```c CS104_Slave_setMaxOpenConnections(slave, 2); ``` ```c CS104_Slave_setLocalPort(slave, 2405); ``` ```c CS104_Slave_setLocalAddress(slave, "192.168.1.50"); ``` -------------------------------- ### Platform and Dependency Configuration Source: https://github.com/mz-automation/lib60870/blob/master/lib60870-C/src/hal/CMakeLists.txt Initializes platform endianness detection and checks for the presence of WinPcap on Windows systems to enable Ethernet support. ```cmake include (TestBigEndian) test_big_endian(PLATFORM_IS_BIGENDIAN) if(WIN32) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib") message("Found winpcap -> compile ethernet HAL layer (required for GOOSE/SV support)") set(WITH_WPCAP 1) include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Include") else() message("winpcap not found -> skip ethernet HAL layer (no L2 GOOSE/SV support)") endif() endif(WIN32) ``` -------------------------------- ### CMake Build Configuration for multi_client_server Source: https://github.com/mz-automation/lib60870/blob/master/lib60870-C/examples/multi_client_server/CMakeLists.txt This CMakeLists.txt file configures the build process for the multi_client_server executable. It specifies include directories, source files, and links against the lib60870 library. ```cmake include_directories( .) set(example_SRCS multi_client_server.c ) IF(WIN32) set_source_files_properties(${example_SRCS} PROPERTIES LANGUAGE CXX) ENDIF(WIN32) add_executable(multi_client_server ${example_SRCS} ) target_link_libraries(multi_client_server lib60870 ) ``` -------------------------------- ### Create and Run CS101 Master Source: https://context7.com/mz-automation/lib60870/llms.txt Initializes a CS101 master for serial communication, registers handlers, and enters a loop to process messages and send commands. Ensure the serial port is correctly specified and accessible. ```c #include "cs101_master.h" #include "hal_serial.h" #include "hal_thread.h" #include "hal_time.h" #include #include static bool running = true; void sigint_handler(int signalId) { running = false; } /* Handle received ASDUs */ static bool asduReceivedHandler(void* parameter, int address, CS101_ASDU asdu) { printf("ASDU received from address %i, type: %s\n", address, TypeID_toString(CS101_ASDU_getTypeID(asdu))); if (CS101_ASDU_getTypeID(asdu) == M_ME_NB_1) { for (int i = 0; i < CS101_ASDU_getNumberOfElements(asdu); i++) { MeasuredValueScaled io = (MeasuredValueScaled)CS101_ASDU_getElement(asdu, i); if (io) { printf(" IOA: %i, Value: %i\n", InformationObject_getObjectAddress((InformationObject)io), MeasuredValueScaled_getValue(io)); MeasuredValueScaled_destroy(io); } } } return true; } /* Track link layer state changes */ static void linkLayerStateChanged(void* parameter, int address, LinkLayerState state) { const char* states[] = {"IDLE", "ERROR", "BUSY", "AVAILABLE"}; printf("Link layer state for address %i: %s\n", address, states[state]); } int main(int argc, char** argv) { signal(SIGINT, sigint_handler); const char* serialPort = (argc > 1) ? argv[1] : "/dev/ttyUSB0"; /* Create serial port (9600 baud, 8 data bits, even parity, 1 stop bit) */ SerialPort port = SerialPort_create(serialPort, 9600, 8, 'E', 1); /* Create master in balanced mode */ CS101_Master master = CS101_Master_create(port, NULL, NULL, IEC60870_LINK_LAYER_BALANCED); /* Set addresses */ CS101_Master_setOwnAddress(master, 1); /* Master address */ CS101_Master_useSlaveAddress(master, 3); /* Slave address */ /* Configure link layer parameters */ LinkLayerParameters llParams = CS101_Master_getLinkLayerParameters(master); llParams->addressLength = 1; llParams->useSingleCharACK = false; /* Register callbacks */ CS101_Master_setASDUReceivedHandler(master, asduReceivedHandler, NULL); CS101_Master_setLinkLayerStateChanged(master, linkLayerStateChanged, NULL); if (!SerialPort_open(port)) { printf("Failed to open serial port\n"); return 1; } int cycle = 0; while (running) { /* Must be called periodically to process messages */ CS101_Master_run(master); if (cycle == 100) { CS101_Master_sendInterrogationCommand(master, CS101_COT_ACTIVATION, 1, IEC60870_QOI_STATION); } if (cycle == 500) { InformationObject sc = (InformationObject)SingleCommand_create(NULL, 5000, true, false, 0); CS101_Master_sendProcessCommand(master, CS101_COT_ACTIVATION, 1, sc); InformationObject_destroy(sc); } if (cycle == 800) { struct sCP56Time2a newTime; CP56Time2a_createFromMsTimestamp(&newTime, Hal_getTimeInMs()); CS101_Master_sendClockSyncCommand(master, 1, &newTime); } Thread_sleep(10); cycle++; } CS101_Master_destroy(master); SerialPort_close(port); SerialPort_destroy(port); return 0; } ``` -------------------------------- ### Download and extract mbedtls 3.6 Source: https://github.com/mz-automation/lib60870/blob/master/lib60870-C/dependencies/README.md Commands to fetch and unpack the mbedtls 3.6.2 source tarball. ```bash wget https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-3.6.2/mbedtls-3.6.2.tar.bz2 tar xfj mbedtls-3.6.2.tar.bz2 ``` -------------------------------- ### Download and extract mbedtls 2.28 Source: https://github.com/mz-automation/lib60870/blob/master/lib60870-C/dependencies/README.md Commands to fetch and unpack the mbedtls 2.28.10 source tarball. ```bash wget https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-2.28.10/mbedtls-2.28.10.tar.bz2 tar xfj mbedtls-2.28.10.tar.bz2 ``` -------------------------------- ### Create and Send Clock Synchronization Command (CS101) Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Synchronizes the clock of a controlled station using a CP56Time2a timestamp with dynamic memory allocation. Requires a master object. ```c CP56Time2a currentTime = CP56Time2a_createFromMsTimestamp(NULL, Hal_getTimeInMs()); CS101_Master_sendClockSyncCommand(master, 1 /* CA */, currentTime); ``` -------------------------------- ### SingleCommand Data Type Constructor Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Defines the parameters for creating a single command object, including IOA, command state, select flag, and qualifier. ```c SingleCommand SingleCommand_create(SingleCommand self, int ioa, bool command, bool selectCommand, int qu); ``` -------------------------------- ### Initialize CS101 Serial Slave Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Creates a serial port object and initializes a CS101 slave instance with link layer parameters. ```c SerialPort port = SerialPort_create(serialPort, 9600, 8, 'E', 1); ``` ```c CS101_Slave slave = CS101_Slave_create(port, NULL, NULL, IEC60870_LINK_LAYER_UNBALANCED); ``` ```c CS101_Slave CS101_Slave_create(SerialPort serialPort, LinkLayerParameters llParameters, CS101_AppLayerParameters alParameters, IEC60870_LinkLayerMode linkLayerMode) ``` -------------------------------- ### Create and Configure CS101 Slave Source: https://context7.com/mz-automation/lib60870/llms.txt Initializes a CS101 slave for serial communication, sets link layer and application layer parameters, and registers handlers for interrogation and reset commands. Ensure the serial port is opened successfully before proceeding. ```c #include "cs101_slave.h" #include "hal_serial.h" #include "hal_thread.h" #include "hal_time.h" #include #include static bool running = true; void sigint_handler(int signalId) { running = false; } /* Handle interrogation commands */ static bool interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) { printf("Interrogation received for group %i\n", qoi); if (CS101_ASDU_getCA(asdu) == 1 && qoi == IEC60870_QOI_STATION) { /* Send confirmation */ IMasterConnection_sendACT_CON(connection, asdu, false); /* Send response data */ CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection); CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); InformationObject io = (InformationObject)SinglePointInformation_create(NULL, 100, true, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(newAsdu, io); CS101_ASDU_addInformationObject(newAsdu, (InformationObject)SinglePointInformation_create((SinglePointInformation)io, 101, false, IEC60870_QUALITY_GOOD)); InformationObject_destroy(io); IMasterConnection_sendASDU(connection, newAsdu); CS101_ASDU_destroy(newAsdu); /* Send termination */ newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_ACTIVATION_TERMINATION, 0, 1, false, false); io = (InformationObject)InterrogationCommand_create(NULL, 0, IEC60870_QOI_STATION); CS101_ASDU_addInformationObject(newAsdu, io); InformationObject_destroy(io); IMasterConnection_sendASDU(connection, newAsdu); CS101_ASDU_destroy(newAsdu); } return true; } /* Handle link layer reset */ static void resetCUHandler(void* parameter) { printf("Reset CU received\n"); CS101_Slave_flushQueues((CS101_Slave)parameter); } int main(int argc, char** argv) { signal(SIGINT, sigint_handler); const char* serialPort = (argc > 1) ? argv[1] : "/dev/ttyUSB0"; SerialPort port = SerialPort_create(serialPort, 9600, 8, 'E', 1); /* Create slave in unbalanced mode */ CS101_Slave slave = CS101_Slave_create(port, NULL, NULL, IEC60870_LINK_LAYER_UNBALANCED); CS101_Slave_setLinkLayerAddress(slave, 1); /* Configure application layer parameters */ CS101_AppLayerParameters alParams = CS101_Slave_getAppLayerParameters(slave); alParams->sizeOfCA = 2; alParams->sizeOfIOA = 3; alParams->sizeOfCOT = 2; /* Configure link layer parameters */ LinkLayerParameters llParams = CS101_Slave_getLinkLayerParameters(slave); llParams->addressLength = 1; llParams->timeoutForAck = 500; /* Register handlers */ CS101_Slave_setInterrogationHandler(slave, interrogationHandler, NULL); CS101_Slave_setResetCUHandler(slave, resetCUHandler, slave); CS101_Slave_setIdleTimeout(slave, 1500); if (!SerialPort_open(port)) { printf("Failed to open serial port\n"); return 1; } int16_t value = 0; uint64_t lastSent = 0; while (running) { CS101_Slave_run(slave); if (Hal_getTimeInMs() > lastSent + 1000) { CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); InformationObject io = (InformationObject)MeasuredValueScaled_create(NULL, 110, value++, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(newAsdu, io); InformationObject_destroy(io); /* Enqueue as class 2 data (background data) */ CS101_Slave_enqueueUserDataClass2(slave, newAsdu); CS101_ASDU_destroy(newAsdu); lastSent = Hal_getTimeInMs(); } Thread_sleep(1); } CS101_Slave_destroy(slave); SerialPort_close(port); SerialPort_destroy(port); return 0; } ``` -------------------------------- ### Create Various ASDUs in C Source: https://context7.com/mz-automation/lib60870/llms.txt This C code demonstrates how to create different types of ASDUs, including single point information, measured values with sequences, floating point values with timestamps, and static ASDUs. It covers adding information objects and printing ASDU details. Ensure necessary headers are included and CS101_AppLayerParameters are properly initialized. ```c #include "iec60870_common.h" #include "cs101_information_objects.h" #include void create_various_asdus(CS101_AppLayerParameters alParams) { /* Create ASDU with single point information (M_SP_NA_1) */ CS101_ASDU asdu1 = CS101_ASDU_create(alParams, false, CS101_COT_SPONTANEOUS, 0, 1, false, false); SinglePointInformation spi = SinglePointInformation_create(NULL, 1001, true, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(asdu1, (InformationObject)spi); SinglePointInformation_destroy(spi); printf("ASDU 1 - Type: %s, COT: %s, Elements: %i\n", TypeID_toString(CS101_ASDU_getTypeID(asdu1)), CS101_CauseOfTransmission_toString(CS101_ASDU_getCOT(asdu1)), CS101_ASDU_getNumberOfElements(asdu1)); CS101_ASDU_destroy(asdu1); /* Create ASDU with measured values (M_ME_NB_1) as a sequence */ CS101_ASDU asdu2 = CS101_ASDU_create(alParams, true, CS101_COT_PERIODIC, 0, 1, false, false); /* In sequence mode, IOAs must be consecutive */ MeasuredValueScaled mv = MeasuredValueScaled_create(NULL, 2000, 100, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(asdu2, (InformationObject)mv); CS101_ASDU_addInformationObject(asdu2, (InformationObject)MeasuredValueScaled_create(mv, 2001, 200, IEC60870_QUALITY_GOOD)); CS101_ASDU_addInformationObject(asdu2, (InformationObject)MeasuredValueScaled_create(mv, 2002, 300, IEC60870_QUALITY_GOOD)); MeasuredValueScaled_destroy(mv); printf("ASDU 2 - Type: %s, Sequence: %s, Elements: %i\n", TypeID_toString(CS101_ASDU_getTypeID(asdu2)), CS101_ASDU_isSequence(asdu2) ? "yes" : "no", CS101_ASDU_getNumberOfElements(asdu2)); CS101_ASDU_destroy(asdu2); /* Create ASDU with floating point values and timestamp (M_ME_TF_1) */ CS101_ASDU asdu3 = CS101_ASDU_create(alParams, false, CS101_COT_SPONTANEOUS, 0, 1, false, false); struct sCP56Time2a timestamp; CP56Time2a_createFromMsTimestamp(×tamp, Hal_getTimeInMs()); MeasuredValueShortWithCP56Time2a mvf = MeasuredValueShortWithCP56Time2a_create(NULL, 3000, 25.5f, IEC60870_QUALITY_GOOD, ×tamp); CS101_ASDU_addInformationObject(asdu3, (InformationObject)mvf); MeasuredValueShortWithCP56Time2a_destroy(mvf); printf("ASDU 3 - Type: %s, CA: %i\n", TypeID_toString(CS101_ASDU_getTypeID(asdu3)), CS101_ASDU_getCA(asdu3)); CS101_ASDU_destroy(asdu3); /* Create static ASDU (no dynamic memory allocation) */ sCS101_StaticASDU staticASDU; CS101_ASDU asdu4 = CS101_ASDU_initializeStatic(&staticASDU, alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); DoublePointInformation dpi = DoublePointInformation_create(NULL, 4000, IEC60870_DOUBLE_POINT_ON, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(asdu4, (InformationObject)dpi); DoublePointInformation_destroy(dpi); printf("ASDU 4 (static) - Type: %s\n", TypeID_toString(CS101_ASDU_getTypeID(asdu4))); /* Note: static ASDU does not need CS101_ASDU_destroy */ } ``` -------------------------------- ### Create and Send Clock Synchronization Command (CS104) Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Synchronizes the clock of a controlled station using a CP56Time2a timestamp. Requires a valid connection object. ```c struct sCP56Time2a currentTime; CP56Time2a_createFromMsTimestamp(¤tTime, Hal_getTimeInMs()); CS104_Connection_sendClockSyncCommand(con, 1 /* CA */, ¤tTime); ``` -------------------------------- ### Connect to CS104 Server Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Establishes a connection to the CS 104 server using the provided connection object. After a successful connection, you can send commands and receive data. ```c CS104_Connection_connect(con); ``` -------------------------------- ### Create Balanced CS101 Master Instance Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Creates a new CS101 master instance for balanced communication mode. The link layer mode parameter is different from the unbalanced mode. The slave address only needs to be set once. ```c CS101_Master master = CS101_Master_create(port, NULL, NULL, IEC60870_LINK_LAYER_BALANCED); ``` -------------------------------- ### Create CS101 ASDU for Periodic Data Source: https://github.com/mz-automation/lib60870/blob/master/user_guide.adoc Creates a new ASDU instance for periodic data transmission. Ensure the 'alParameters' are correctly configured for your application. ```c CS101_ASDU newAsdu = CS101_ASDU_create(alParameters, false, CS101_COT_PERIODIC, 0, 1, false, false); ```