/* * Copyright 2016-2022 Michael Zillgith * * This file is part of lib60870-C * * lib60870-C is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * lib60870-C is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with lib60870-C. If not, see . * * See COPYING file for the complete license text. */ #ifndef _IEC60870_COMMON_H_ #define _IEC60870_COMMON_H_ #include #include #include "iec_include.h" #ifdef __cplusplus extern "C" { #endif /** * \file iec60870_common.h * \brief Common definitions for IEC 60870-5-101/104 * These types are used by CS101/CS104 master and slaves */ /** * @addtogroup COMMON Common API functions * * @{ */ #define IEC_60870_5_104_DEFAULT_PORT 2404 #define IEC_60870_5_104_DEFAULT_TLS_PORT 19998 #define LIB60870_VERSION_MAJOR 2 #define LIB60870_VERSION_MINOR 3 #define LIB60870_VERSION_PATCH 3 /** * \brief lib60870 version information */ typedef struct { int major; int minor; int patch; } Lib60870VersionInfo; /** * \brief link layer mode for serial link layers */ typedef enum { IEC60870_LINK_LAYER_BALANCED = 0, IEC60870_LINK_LAYER_UNBALANCED = 1 } IEC60870_LinkLayerMode; /** \brief State of the link layer */ typedef enum { /** The link layer is idle, there is no communication */ LL_STATE_IDLE, /** An error has occurred at the link layer, the link may not be usable */ LL_STATE_ERROR, /** The link layer is busy and therefore no usable */ LL_STATE_BUSY, /** The link is available for user data transmission and reception */ LL_STATE_AVAILABLE } LinkLayerState; /** * \brief Callback handler for link layer state changes * * \param parameter user provided parameter that is passed to the handler * \param address slave address used by the link layer state machine (only relevant for unbalanced master) * \param newState the new link layer state */ typedef void (*IEC60870_LinkLayerStateChangedHandler) (void* parameter, int address, LinkLayerState newState); /** * \brief Callback handler for sent and received messages * * This callback handler provides access to the raw message buffer of received or sent * messages. It can be used for debugging purposes. Usually it is not used nor required * for applications. * * \param parameter user provided parameter * \param msg the message buffer * \param msgSize size of the message * \param sent indicates if the message was sent or received */ typedef void (*IEC60870_RawMessageHandler) (void* parameter, uint8_t* msg, int msgSize, bool sent); /** * \brief Parameters for the CS101/CS104 application layer */ typedef struct sCS101_AppLayerParameters* CS101_AppLayerParameters; struct sCS101_AppLayerParameters { int sizeOfTypeId; /* size of the type id (default = 1 - don't change) */ int sizeOfVSQ; /* don't change */ int sizeOfCOT; /* size of COT (1/2 - default = 2 -> COT includes OA) */ int originatorAddress; /* originator address (OA) to use (0-255) */ int sizeOfCA; /* size of common address (CA) of ASDU (1/2 - default = 2) */ int sizeOfIOA; /* size of information object address (IOA) (1/2/3 - default = 3) */ int maxSizeOfASDU; /* maximum size of the ASDU that is generated - the maximum maximum value is 249 for IEC 104 and 254 for IEC 101 */ }; /** * \brief Message type IDs */ typedef enum { M_SP_NA_1 = 1, M_SP_TA_1 = 2, M_DP_NA_1 = 3, M_DP_TA_1 = 4, M_ST_NA_1 = 5, M_ST_TA_1 = 6, M_BO_NA_1 = 7, M_BO_TA_1 = 8, M_ME_NA_1 = 9, M_ME_TA_1 = 10, M_ME_NB_1 = 11, M_ME_TB_1 = 12, M_ME_NC_1 = 13, M_ME_TC_1 = 14, M_IT_NA_1 = 15, M_IT_TA_1 = 16, M_EP_TA_1 = 17, M_EP_TB_1 = 18, M_EP_TC_1 = 19, M_PS_NA_1 = 20, M_ME_ND_1 = 21, M_SP_TB_1 = 30, M_DP_TB_1 = 31, M_ST_TB_1 = 32, M_BO_TB_1 = 33, M_ME_TD_1 = 34, M_ME_TE_1 = 35, M_ME_TF_1 = 36, M_IT_TB_1 = 37, M_EP_TD_1 = 38, M_EP_TE_1 = 39, M_EP_TF_1 = 40, S_IT_TC_1 = 41, C_SC_NA_1 = 45, C_DC_NA_1 = 46, C_RC_NA_1 = 47, C_SE_NA_1 = 48, C_SE_NB_1 = 49, C_SE_NC_1 = 50, C_BO_NA_1 = 51, C_SC_TA_1 = 58, C_DC_TA_1 = 59, C_RC_TA_1 = 60, C_SE_TA_1 = 61, C_SE_TB_1 = 62, C_SE_TC_1 = 63, C_BO_TA_1 = 64, M_EI_NA_1 = 70, S_CH_NA_1 = 81, S_RP_NA_1 = 82, S_AR_NA_1 = 83, S_KR_NA_1 = 84, S_KS_NA_1 = 85, S_KC_NA_1 = 86, S_ER_NA_1 = 87, S_US_NA_1 = 90, S_UQ_NA_1 = 91, S_UR_NA_1 = 92, S_UK_NA_1 = 93, S_UA_NA_1 = 94, S_UC_NA_1 = 95, C_IC_NA_1 = 100, C_CI_NA_1 = 101, C_RD_NA_1 = 102, C_CS_NA_1 = 103, C_TS_NA_1 = 104, C_RP_NA_1 = 105, C_CD_NA_1 = 106, C_TS_TA_1 = 107, P_ME_NA_1 = 110, P_ME_NB_1 = 111, P_ME_NC_1 = 112, P_AC_NA_1 = 113, F_FR_NA_1 = 120, F_SR_NA_1 = 121, F_SC_NA_1 = 122, F_LS_NA_1 = 123, F_AF_NA_1 = 124, F_SG_NA_1 = 125, F_DR_TA_1 = 126, F_SC_NB_1 = 127 } IEC60870_5_TypeID; typedef IEC60870_5_TypeID TypeID; typedef struct sInformationObject* InformationObject; /** * \brief Application Service Data Unit (ASDU) for the CS101/CS104 application layer */ typedef struct sCS101_ASDU* CS101_ASDU; typedef struct { CS101_AppLayerParameters parameters; uint8_t* asdu; int asduHeaderLength; uint8_t* payload; int payloadSize; uint8_t encodedData[256]; } sCS101_StaticASDU; typedef sCS101_StaticASDU* CS101_StaticASDU; typedef struct sCP16Time2a* CP16Time2a; struct sCP16Time2a { uint8_t encodedValue[2]; }; typedef struct sCP24Time2a* CP24Time2a; struct sCP24Time2a { uint8_t encodedValue[3]; }; typedef struct sCP32Time2a* CP32Time2a; /** * \brief 4 byte binary time */ struct sCP32Time2a { uint8_t encodedValue[4]; }; /** * \brief 7 byte binary time */ typedef struct sCP56Time2a* CP56Time2a; struct sCP56Time2a { uint8_t encodedValue[7]; }; /** * \brief Base type for counter readings */ typedef struct sBinaryCounterReading* BinaryCounterReading; struct sBinaryCounterReading { uint8_t encodedValue[5]; }; /** * \brief Parameters for CS104 connections - APCI (application protocol control information) */ typedef struct sCS104_APCIParameters* CS104_APCIParameters; struct sCS104_APCIParameters { int k; int w; int t0; int t1; int t2; int t3; }; #include "cs101_information_objects.h" typedef enum { CS101_COT_PERIODIC = 1, CS101_COT_BACKGROUND_SCAN = 2, CS101_COT_SPONTANEOUS = 3, CS101_COT_INITIALIZED = 4, CS101_COT_REQUEST = 5, CS101_COT_ACTIVATION = 6, CS101_COT_ACTIVATION_CON = 7, CS101_COT_DEACTIVATION = 8, CS101_COT_DEACTIVATION_CON = 9, CS101_COT_ACTIVATION_TERMINATION = 10, CS101_COT_RETURN_INFO_REMOTE = 11, CS101_COT_RETURN_INFO_LOCAL = 12, CS101_COT_FILE_TRANSFER = 13, CS101_COT_AUTHENTICATION = 14, CS101_COT_MAINTENANCE_OF_AUTH_SESSION_KEY = 15, CS101_COT_MAINTENANCE_OF_USER_ROLE_AND_UPDATE_KEY = 16, CS101_COT_INTERROGATED_BY_STATION = 20, CS101_COT_INTERROGATED_BY_GROUP_1 = 21, CS101_COT_INTERROGATED_BY_GROUP_2 = 22, CS101_COT_INTERROGATED_BY_GROUP_3 = 23, CS101_COT_INTERROGATED_BY_GROUP_4 = 24, CS101_COT_INTERROGATED_BY_GROUP_5 = 25, CS101_COT_INTERROGATED_BY_GROUP_6 = 26, CS101_COT_INTERROGATED_BY_GROUP_7 = 27, CS101_COT_INTERROGATED_BY_GROUP_8 = 28, CS101_COT_INTERROGATED_BY_GROUP_9 = 29, CS101_COT_INTERROGATED_BY_GROUP_10 = 30, CS101_COT_INTERROGATED_BY_GROUP_11 = 31, CS101_COT_INTERROGATED_BY_GROUP_12 = 32, CS101_COT_INTERROGATED_BY_GROUP_13 = 33, CS101_COT_INTERROGATED_BY_GROUP_14 = 34, CS101_COT_INTERROGATED_BY_GROUP_15 = 35, CS101_COT_INTERROGATED_BY_GROUP_16 = 36, CS101_COT_REQUESTED_BY_GENERAL_COUNTER = 37, CS101_COT_REQUESTED_BY_GROUP_1_COUNTER = 38, CS101_COT_REQUESTED_BY_GROUP_2_COUNTER = 39, CS101_COT_REQUESTED_BY_GROUP_3_COUNTER = 40, CS101_COT_REQUESTED_BY_GROUP_4_COUNTER = 41, CS101_COT_UNKNOWN_TYPE_ID = 44, CS101_COT_UNKNOWN_COT = 45, CS101_COT_UNKNOWN_CA = 46, CS101_COT_UNKNOWN_IOA = 47 } CS101_CauseOfTransmission; const char* CS101_CauseOfTransmission_toString(CS101_CauseOfTransmission self); void Lib60870_enableDebugOutput(bool value); Lib60870VersionInfo Lib60870_getLibraryVersionInfo(void); /** * \brief Check if the test flag of the ASDU is set */ bool CS101_ASDU_isTest(CS101_ASDU self); /** * \brief Set the test flag of the ASDU */ void CS101_ASDU_setTest(CS101_ASDU self, bool value); /** * \brief Check if the negative flag of the ASDU is set */ bool CS101_ASDU_isNegative(CS101_ASDU self); /** * \brief Set the negative flag of the ASDU */ void CS101_ASDU_setNegative(CS101_ASDU self, bool value); /** * \brief get the OA (originator address) of the ASDU. */ int CS101_ASDU_getOA(CS101_ASDU self); /** * \brief Get the cause of transmission (COT) of the ASDU */ CS101_CauseOfTransmission CS101_ASDU_getCOT(CS101_ASDU self); /** * \brief Set the cause of transmission (COT) of the ASDU */ void CS101_ASDU_setCOT(CS101_ASDU self, CS101_CauseOfTransmission value); /** * \brief Get the common address (CA) of the ASDU */ int CS101_ASDU_getCA(CS101_ASDU self); /** * \brief Set the common address (CA) of the ASDU * * \param ca the ca in unstructured form */ void CS101_ASDU_setCA(CS101_ASDU self, int ca); /** * \brief Get the type ID of the ASDU * * \return the type ID to identify the ASDU type */ IEC60870_5_TypeID CS101_ASDU_getTypeID(CS101_ASDU self); /** * \brief Set the type ID of the ASDU * * NOTE: Usually it is not required to call this function as the type is determined when the * ASDU is parsed or when a new information object is added with \ref CS101_ASDU_addInformationObject * The function is intended to be used by library extensions of for creating private ASDU types. * * \param typeId the type ID to identify the ASDU type */ void CS101_ASDU_setTypeID(CS101_ASDU self, IEC60870_5_TypeID typeId); /** * \brief Check if the ASDU contains a sequence of consecutive information objects * * NOTE: in a sequence of consecutive information objects only the first information object address * is encoded. The following information objects ahve consecutive information object addresses. * * \return true when the ASDU represents a sequence of consecutive information objects, false otherwise */ bool CS101_ASDU_isSequence(CS101_ASDU self); /** * \brief Set the ASDU to represent a sequence of consecutive information objects * * NOTE: It is not required to use this function when constructing ASDUs as this information is * already provided when calling one of the constructors \ref CS101_ASDU_create or \ref CS101_ASDU_initializeStatic * * \param isSequence specify if the ASDU represents a sequence of consecutive information objects */ void CS101_ASDU_setSequence(CS101_ASDU self, bool isSequence); /** * \brief Get the number of information objects (elements) in the ASDU * * \return the number of information objects/element (valid range 0 - 127) */ int CS101_ASDU_getNumberOfElements(CS101_ASDU self); /** * \brief Set the number of information objects (elements) in the ASDU * * NOTE: Usually it is not required to call this function as the number of elements is set when the * ASDU is parsed or when a new information object is added with \ref CS101_ASDU_addInformationObject * The function is intended to be used by library extensions of for creating private ASDU types. * * \param numberOfElements the number of information objects/element (valid range 0 - 127) */ void CS101_ASDU_setNumberOfElements(CS101_ASDU self, int numberOfElements); /** * \brief Get the information object with the given index * * \param index the index of the information object (starting with 0) * * \return the information object, or NULL if there is no information object with the given index */ InformationObject CS101_ASDU_getElement(CS101_ASDU self, int index); /** * \brief Get the information object with the given index and store it in the provided information object instance * * \param io if not NULL use the provided information object instance to store the information, has to be of correct type. * \param index the index of the information object (starting with 0) * * \return the information object, or NULL if there is no information object with the given index */ InformationObject CS101_ASDU_getElementEx(CS101_ASDU self, InformationObject io, int index); /** * \brief Create a new ASDU. The type ID will be derived from the first InformationObject that will be added * * \param parameters the application layer parameters used to encode the ASDU * \param isSequence if the information objects will be encoded as a compact sequence of information objects with subsequent IOA values * \param cot cause of transmission (COT) * \param oa originator address (OA) to be used * \param ca the common address (CA) of the ASDU * \param isTest if the test flag will be set or not * \param isNegative if the negative falg will be set or not * * \return the new CS101_ASDU instance */ CS101_ASDU CS101_ASDU_create(CS101_AppLayerParameters parameters, bool isSequence, CS101_CauseOfTransmission cot, int oa, int ca, bool isTest, bool isNegative); /** * \brief Create a new ASDU and store it in the provided static ASDU structure. * * NOTE: The type ID will be derived from the first InformationObject that will be added. * * \param self pointer to the statically allocated data structure * \param parameters the application layer parameters used to encode the ASDU * \param isSequence if the information objects will be encoded as a compact sequence of information objects with subsequent IOA values * \param cot cause of transmission (COT) * \param oa originator address (OA) to be used * \param ca the common address (CA) of the ASDU * \param isTest if the test flag will be set or not * \param isNegative if the negative falg will be set or not * * \return the new CS101_ASDU instance */ CS101_ASDU CS101_ASDU_initializeStatic(CS101_StaticASDU self, CS101_AppLayerParameters parameters, bool isSequence, CS101_CauseOfTransmission cot, int oa, int ca, bool isTest, bool isNegative); /** * \brief Create a new ASDU that is an exact copy of the ASDU * * \param self ASDU instance to be copied * \param clone static ASDU instance where to store the cloned ASDU or NULL. When this parameter is NULL the function will allocate the memory for the clone * * \return the cloned ASDU instance */ CS101_ASDU CS101_ASDU_clone(CS101_ASDU self, CS101_StaticASDU clone); /** * Get the ASDU payload * * The payload is the ASDU message part after the ASDU header (type ID, VSQ, COT, CASDU) * * \return the ASDU payload buffer */ uint8_t* CS101_ASDU_getPayload(CS101_ASDU self); /** * \brief Append the provided data to the ASDU payload * * NOTE: Usually it is not required to call this function as the payload is set when the * ASDU is parsed or when a new information object is added with \ref CS101_ASDU_addInformationObject * The function is intended to be only used by library extensions of for creating private ASDU types. * * \param buffer pointer to the payload data to add * \param size number of bytes to append to the payload * * \return true when data has been added, false otherwise */ bool CS101_ASDU_addPayload(CS101_ASDU self, uint8_t* buffer, int size); /** * Get the ASDU payload buffer size * * The payload is the ASDU message part after the ASDU header (type ID, VSQ, COT, CASDU) * * \return the ASDU payload buffer size */ int CS101_ASDU_getPayloadSize(CS101_ASDU self); /** * \brief Destroy the ASDU object (release all resources) */ void CS101_ASDU_destroy(CS101_ASDU self); /** * \brief add an information object to the ASDU * * NOTE: Only information objects of the exact same type can be added to a single ASDU! * * \param self ASDU object instance * \param io information object to be added * * \return true when added, false when there not enough space left in the ASDU or IO cannot be added to the sequence because of wrong IOA, or wrong type. */ bool CS101_ASDU_addInformationObject(CS101_ASDU self, InformationObject io); /** * \brief remove all information elements from the ASDU object * * \param self ASDU object instance */ void CS101_ASDU_removeAllElements(CS101_ASDU self); /** * \brief Get the elapsed time in ms */ int CP16Time2a_getEplapsedTimeInMs(const CP16Time2a self); /** * \brief set the elapsed time in ms */ void CP16Time2a_setEplapsedTimeInMs(CP16Time2a self, int value); /** * \brief Get the millisecond part of the time value */ int CP24Time2a_getMillisecond(const CP24Time2a self); /** * \brief Set the millisecond part of the time value */ void CP24Time2a_setMillisecond(CP24Time2a self, int value); /** * \brief Get the second part of the time value */ int CP24Time2a_getSecond(const CP24Time2a self); /** * \brief Set the second part of the time value */ void CP24Time2a_setSecond(CP24Time2a self, int value); /** * \brief Get the minute part of the time value */ int CP24Time2a_getMinute(const CP24Time2a self); /** * \brief Set the minute part of the time value */ void CP24Time2a_setMinute(CP24Time2a self, int value); /** * \brief Check if the invalid flag of the time value is set */ bool CP24Time2a_isInvalid(const CP24Time2a self); /** * \brief Set the invalid flag of the time value */ void CP24Time2a_setInvalid(CP24Time2a self, bool value); /** * \brief Check if the substituted flag of the time value is set */ bool CP24Time2a_isSubstituted(const CP24Time2a self); /** * \brief Set the substituted flag of the time value */ void CP24Time2a_setSubstituted(CP24Time2a self, bool value); /** * \brief Create a 7 byte time from a UTC ms timestamp */ CP56Time2a CP56Time2a_createFromMsTimestamp(CP56Time2a self, uint64_t timestamp); CP32Time2a CP32Time2a_create(CP32Time2a self); void CP32Time2a_setFromMsTimestamp(CP32Time2a self, uint64_t timestamp); int CP32Time2a_getMillisecond(const CP32Time2a self); void CP32Time2a_setMillisecond(CP32Time2a self, int value); int CP32Time2a_getSecond(const CP32Time2a self); void CP32Time2a_setSecond(CP32Time2a self, int value); int CP32Time2a_getMinute(const CP32Time2a self); void CP32Time2a_setMinute(CP32Time2a self, int value); bool CP32Time2a_isInvalid(const CP32Time2a self); void CP32Time2a_setInvalid(CP32Time2a self, bool value); bool CP32Time2a_isSubstituted(const CP32Time2a self); void CP32Time2a_setSubstituted(CP32Time2a self, bool value); int CP32Time2a_getHour(const CP32Time2a self); void CP32Time2a_setHour(CP32Time2a self, int value); bool CP32Time2a_isSummerTime(const CP32Time2a self); void CP32Time2a_setSummerTime(CP32Time2a self, bool value); /** * \brief Set the time value of a 7 byte time from a UTC ms timestamp */ void CP56Time2a_setFromMsTimestamp(CP56Time2a self, uint64_t timestamp); /** * \brief Convert a 7 byte time to a ms timestamp */ uint64_t CP56Time2a_toMsTimestamp(const CP56Time2a self); /** * \brief Get the ms part of a time value */ int CP56Time2a_getMillisecond(const CP56Time2a self); /** * \brief Set the ms part of a time value */ void CP56Time2a_setMillisecond(CP56Time2a self, int value); int CP56Time2a_getSecond(const CP56Time2a self); void CP56Time2a_setSecond(CP56Time2a self, int value); int CP56Time2a_getMinute(const CP56Time2a self); void CP56Time2a_setMinute(CP56Time2a self, int value); int CP56Time2a_getHour(const CP56Time2a self); void CP56Time2a_setHour(CP56Time2a self, int value); int CP56Time2a_getDayOfWeek(const CP56Time2a self); void CP56Time2a_setDayOfWeek(CP56Time2a self, int value); int CP56Time2a_getDayOfMonth(const CP56Time2a self); void CP56Time2a_setDayOfMonth(CP56Time2a self, int value); /** * \brief Get the month field of the time * * \return value the month (1..12) */ int CP56Time2a_getMonth(const CP56Time2a self); /** * \brief Set the month field of the time * * \param value the month (1..12) */ void CP56Time2a_setMonth(CP56Time2a self, int value); /** * \brief Get the year (range 0..99) * * \param value the year (0..99) */ int CP56Time2a_getYear(const CP56Time2a self); /** * \brief Set the year * * \param value the year */ void CP56Time2a_setYear(CP56Time2a self, int value); bool CP56Time2a_isSummerTime(const CP56Time2a self); void CP56Time2a_setSummerTime(CP56Time2a self, bool value); bool CP56Time2a_isInvalid(const CP56Time2a self); void CP56Time2a_setInvalid(CP56Time2a self, bool value); bool CP56Time2a_isSubstituted(const CP56Time2a self); void CP56Time2a_setSubstituted(CP56Time2a self, bool value); BinaryCounterReading BinaryCounterReading_create(BinaryCounterReading self, int32_t value, int seqNumber, bool hasCarry, bool isAdjusted, bool isInvalid); void BinaryCounterReading_destroy(BinaryCounterReading self); int32_t BinaryCounterReading_getValue(BinaryCounterReading self); void BinaryCounterReading_setValue(BinaryCounterReading self, int32_t value); int BinaryCounterReading_getSequenceNumber(BinaryCounterReading self); bool BinaryCounterReading_hasCarry(BinaryCounterReading self); bool BinaryCounterReading_isAdjusted(BinaryCounterReading self); bool BinaryCounterReading_isInvalid(BinaryCounterReading self); void BinaryCounterReading_setSequenceNumber(BinaryCounterReading self, int value); void BinaryCounterReading_setCarry(BinaryCounterReading self, bool value); void BinaryCounterReading_setAdjusted(BinaryCounterReading self, bool value); void BinaryCounterReading_setInvalid(BinaryCounterReading self, bool value); /** * @} */ typedef struct sFrame* Frame; void Frame_destroy(Frame self); void Frame_resetFrame(Frame self); void Frame_setNextByte(Frame self, uint8_t byte); void Frame_appendBytes(Frame self, uint8_t* bytes, int numberOfBytes); int Frame_getMsgSize(Frame self); uint8_t* Frame_getBuffer(Frame self); int Frame_getSpaceLeft(Frame self); #ifdef __cplusplus } #endif #endif /* SRC_INC_IEC60870_COMMON_H_ */