diff --git a/CHANGELOG b/CHANGELOG index d625cbc..3978272 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,19 @@ +Changes to version 0.9 +---------------------- +- Sampled values subscriber code +- Experimental sampled values publisher and MMS SVCB code +- server: support for VMD scope data sets (MMS named variable lists) +- .NET API: added MmsConnection.setLocalDetail and MmsConnection.getLocalDetail +- example server/sv publisher according to IEC 61850-9-2LE +- server: multi-threaded configuration now uses select instead of socket polling +- .NET API: some additional methods for MmsValue class +- server: fixed bug with nested directories in file service +- mms_utility: added read file directory feature +- SCL parser/modelviewer: show unused types in SCL file +- server: fixed some problems related to buffered reporting UCA test cases +- other bug fixes + + Changes to version 0.8.7 ------------------------ - client: added client side control service support for edition 1 and 2 APC CDCs diff --git a/CMakeLists.txt b/CMakeLists.txt index 55ccc69..cbc2f36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,7 @@ set(API_HEADERS src/goose/goose_subscriber.h src/goose/goose_receiver.h src/sampled_values/sv_subscriber.h + src/sampled_values/sv_publisher.h ) IF(WIN32) diff --git a/config/stack_config.h b/config/stack_config.h index 61ae9e6..c21730e 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -17,7 +17,7 @@ #define DEBUG_COTP 0 #define DEBUG_ISO_SERVER 0 #define DEBUG_ISO_CLIENT 0 -#define DEBUG_IED_SERVER 1 +#define DEBUG_IED_SERVER 0 #define DEBUG_IED_CLIENT 0 #define DEBUG_MMS_CLIENT 0 #define DEBUG_MMS_SERVER 0 diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake index 36b7ff3..3c44e1f 100644 --- a/config/stack_config.h.cmake +++ b/config/stack_config.h.cmake @@ -72,7 +72,7 @@ #cmakedefine01 CONFIG_INCLUDE_GOOSE_SUPPORT /* Set to 1 to include Sampled Values support in the build. Otherwise set to 0 */ -#cmakedefine01 CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT +#define CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT 1 #ifdef _WIN32 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8e0f41d..99b09e0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -24,3 +24,6 @@ add_subdirectory(mms_client_example3) add_subdirectory(mms_client_example4) add_subdirectory(goose_subscriber) add_subdirectory(sv_subscriber) +add_subdirectory(iec61850_9_2_LE_example) +add_subdirectory(iec61850_sv_client_example) +add_subdirectory(sv_publisher) diff --git a/examples/Makefile b/examples/Makefile index b5f2039..7492d07 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -27,6 +27,10 @@ EXAMPLE_DIRS += goose_subscriber EXAMPLE_DIRS += goose_publisher EXAMPLE_DIRS += sv_subscriber EXAMPLE_DIRS += mms_utility +EXAMPLE_DIRS += iec61850_9_2_LE_example +EXAMPLE_DIRS += iec61850_sv_client_example +EXAMPLE_DIRS += sv_publisher +EXAMPLE_DIRS += sv_subscriber MODEL_DIRS += server_example1 MODEL_DIRS += server_example2 @@ -40,6 +44,7 @@ MODEL_DIRS += server_example_complex_array MODEL_DIRS += server_example_61400_25 MODEL_DIRS += server_example_threadless MODEL_DIRS += server_example_setting_groups +MODEL_DIRS += iec61850_9_2_LE_example all: examples diff --git a/examples/iec61850_9_2_LE_example/CMakeLists.txt b/examples/iec61850_9_2_LE_example/CMakeLists.txt index fdaa929..3351a01 100644 --- a/examples/iec61850_9_2_LE_example/CMakeLists.txt +++ b/examples/iec61850_9_2_LE_example/CMakeLists.txt @@ -3,7 +3,7 @@ include_directories( ) set(sv_9_2_LE_example_SRCS - server_example2.c + iec61850_9_2_LE_example.c static_model.c ) diff --git a/examples/iec61850_9_2_LE_example/Makefile b/examples/iec61850_9_2_LE_example/Makefile index 8437cf3..0e3703a 100644 --- a/examples/iec61850_9_2_LE_example/Makefile +++ b/examples/iec61850_9_2_LE_example/Makefile @@ -4,7 +4,7 @@ PROJECT_BINARY_NAME = sv_9_2LE_example PROJECT_SOURCES = iec61850_9_2_LE_example.c PROJECT_SOURCES += static_model.c -PROJECT_ICD_FILE = complexModel.icd +PROJECT_ICD_FILE = sv.icd include $(LIBIEC_HOME)/make/target_system.mk include $(LIBIEC_HOME)/make/stack_includes.mk diff --git a/examples/server_example_dynamic/server_example_dynamic.c b/examples/server_example_dynamic/server_example_dynamic.c index 2bab1f0..f10f636 100644 --- a/examples/server_example_dynamic/server_example_dynamic.c +++ b/examples/server_example_dynamic/server_example_dynamic.c @@ -83,7 +83,7 @@ int main(int argc, char** argv) { while (running) { IedServer_lockDataModel(iedServer); - IedServer_updateUTCTimeAttributeValue(iedServer, temperatureTimestamp, Hal_getTimeInMs()) + IedServer_updateUTCTimeAttributeValue(iedServer, temperatureTimestamp, Hal_getTimeInMs()); IedServer_updateFloatAttributeValue(iedServer, temperatureValue, val); IedServer_unlockDataModel(iedServer); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8d83534..4b4b801 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -176,6 +176,7 @@ set (lib_goose_SRCS set (lib_sv_SRCS ./sampled_values/sv_subscriber.c +./sampled_values/sv_publisher.c ) set (lib_linux_SRCS diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index e745678..9d6ac1a 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -660,6 +660,18 @@ MmsConnection_fileDelete(MmsConnection self, MmsError* mmsError, const char* fil void MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, const char* currentFileName, const char* newFileName); + +/** + * \brief Send an obtainFile request to the server (used to initiate file download to server) + * + * \param self MmsConnection instance to operate on + * \param mmsError user provided variable to store error code + * \param sourceFile the name of the source file (client side name) + * \param destinationFile the name of the destination file (server side name) + */ +void +MmsConnection_obtainFile(MmsConnection self, MmsError* mmsError, const char* sourceFile, const char* destinationFile); + /** * \brief get the file directory of the server. * diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index fc1ec8b..8406f9c 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -236,6 +236,9 @@ mmsClient_createFileCloseRequest(uint32_t invokeId, ByteBuffer* request, int32_t void mmsClient_createFileRenameRequest(uint32_t invokeId, ByteBuffer* request, const char* currentFileName, const char* newFileName); +void +mmsClient_createObtainFileRequest(uint32_t invokeId, ByteBuffer* request, const char* sourceFile, const char* destinationFile); + void mmsClient_createFileDeleteRequest(uint32_t invokeId, ByteBuffer* request, const char* fileName); diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index c91005c..c514ca8 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -1794,6 +1794,29 @@ MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, const char* cur } +void +MmsConnection_obtainFile(MmsConnection self, MmsError* mmsError, const char* sourceFile, const char* destinationFile) +{ + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + *mmsError = MMS_ERROR_NONE; + + uint32_t invokeId = getNextInvokeId(self); + + mmsClient_createObtainFileRequest(invokeId, payload, sourceFile, destinationFile); + + sendRequestAndWaitForResponse(self, invokeId, payload); + + + if (self->lastResponseError != MMS_ERROR_NONE) + *mmsError = self->lastResponseError; + + releaseResponse(self); + + if (self->associationState == MMS_STATE_CLOSED) + *mmsError = MMS_ERROR_CONNECTION_LOST; +} + void MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, diff --git a/src/mms/iso_mms/client/mms_client_files.c b/src/mms/iso_mms/client/mms_client_files.c index 17c0790..97570bd 100644 --- a/src/mms/iso_mms/client/mms_client_files.c +++ b/src/mms/iso_mms/client/mms_client_files.c @@ -183,7 +183,7 @@ mmsClient_createFileRenameRequest(uint32_t invokeId, ByteBuffer* request, const { uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId); - uint32_t confirmedRequestPduSize = 1 + 2 + 2 + invokeIdSize + 0; + uint32_t confirmedRequestPduSize = 1 + 2 + 2 + invokeIdSize; uint32_t parameterSize = 0; @@ -212,6 +212,41 @@ mmsClient_createFileRenameRequest(uint32_t invokeId, ByteBuffer* request, const request->size = bufPos; } +void +mmsClient_createObtainFileRequest(uint32_t invokeId, ByteBuffer* request, const char* sourceFile, const char* destinationFile) +{ + uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId); + + uint32_t confirmedRequestPduSize = 1 + 2 + 2 + invokeIdSize; + + uint32_t parameterSize = 0; + + parameterSize += encodeFileSpecification(0xa0, sourceFile, NULL, 0); + + parameterSize += encodeFileSpecification(0xa1, destinationFile, NULL, 0); + + confirmedRequestPduSize += parameterSize; + + int bufPos = 0; + uint8_t* buffer = request->buffer; + + bufPos = BerEncoder_encodeTL(0xa0, confirmedRequestPduSize, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x02, invokeIdSize, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos); + + /* Encode ObtainFile tag (context | structured ) [46 = 2eh] */ + buffer[bufPos++] = 0xbf; + buffer[bufPos++] = 0x2e; + bufPos = BerEncoder_encodeLength(parameterSize, buffer, bufPos); + + bufPos = encodeFileSpecification(0xa1, sourceFile, buffer, bufPos); + + bufPos = encodeFileSpecification(0xa2, destinationFile, buffer, bufPos); + + request->size = bufPos; +} + + static bool parseFileAttributes(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* fileSize, uint64_t* lastModified) { diff --git a/src/sampled_values/sv_publisher.c b/src/sampled_values/sv_publisher.c index 2fbdb6d..7960a7c 100644 --- a/src/sampled_values/sv_publisher.c +++ b/src/sampled_values/sv_publisher.c @@ -264,7 +264,7 @@ encodeUtcTime(uint64_t timeval, uint8_t* buffer, int bufPos) SampledValuesPublisher SampledValuesPublisher_create(const char* interfaceId) { - SampledValuesPublisher self = GLOBAL_CALLOC(1, sizeof(struct sSampledValuesPublisher)); + SampledValuesPublisher self = (SampledValuesPublisher) GLOBAL_CALLOC(1, sizeof(struct sSampledValuesPublisher)); self->asduLIst = NULL; @@ -276,7 +276,7 @@ SampledValuesPublisher_create(const char* interfaceId) SV_ASDU SampledValuesPublisher_addASDU(SampledValuesPublisher self, char* svID, char* datset, uint32_t confRev) { - SV_ASDU newAsdu = GLOBAL_CALLOC(1, sizeof(struct sSV_ASDU)); + SV_ASDU newAsdu = (SV_ASDU) GLOBAL_CALLOC(1, sizeof(struct sSV_ASDU)); newAsdu->svID = svID; newAsdu->datset = datset; diff --git a/src/vs/libiec61850.def b/src/vs/libiec61850.def index 8ad4188..7d9842d 100644 --- a/src/vs/libiec61850.def +++ b/src/vs/libiec61850.def @@ -565,4 +565,5 @@ EXPORTS ClientSVControlBlock_getSmpMod ClientSVControlBlock_getNoASDU IedServer_setSVCBHandler - IedModel_getSVControlBlock \ No newline at end of file + IedModel_getSVControlBlock + \ No newline at end of file